使用PyInstaller与Bokeh和CustomJS

时间:2018-04-02 22:02:10

标签: python python-2.7 bokeh pyinstaller py2exe

我正在开发一个使用Bokeh和PubSub以及其他python模块的python应用程序。我几乎完成了它并且它运行良好。但是当我尝试使用PyInstaller从中创建一个可执行文件时,我遇到了很多问题。我使用来自this线程的@dzman帖子解决了Jinja2 TemplateNotFound和未注册的加载器类型问题。我使用this帖子中的@Stefano帖子解决了PubSub导入问题。现在的问题是,当我尝试运行.exe文件时,它会抛出错误,如下所示

Traceback (most recent call last):
File "module1.py", line 191, in <lambda>
File "module1.py", line 274, in getQueryItems
File "module1.py", line 353, in queryTheDatabaseToGetResult
File "module1.py", line 399, in createDataframeFromTheResult
File "module2.py", line 17, in __init__
File "module2.py", line 75, in plotFunction
File "site-packages\bokeh\models\callbacks.py", line 68, in 
from_coffeescript
File "site-packages\bokeh\util\compiler.py", line 190, in nodejs_compile
File "site-packages\bokeh\util\compiler.py", line 169, in _run_nodejs
File "site-packages\bokeh\util\compiler.py", line 164, in _run
RuntimeError: module.js:538
throw err;
^

Error: Cannot find module 'C:\Users\user_name\Desktop\PYTHON~1\dist\PATHWA~1\bokeh\server\static\js\compiler.js'
at Function.Module._resolveFilename (module.js:536:15)
at Function.Module._load (module.js:466:25)
at Function.Module.runMain (module.js:676:10)
at startup (bootstrap_node.js:187:16)
at bootstrap_node.js:608:3

在代码中,错误发生在以下代码段的最后一行,

    checkbox.callback = CustomJS.from_coffeescript(args = dict(plot = fig, checkbox = checkbox), code=""" 

    rends = plot.select("hideable");
    rends[i].visible = i in checkbox.active for i in [0...rends.length];

    """)    #This is line 68 as shown in the error message

因此,它是代码失败的CustomJS。我找不到一篇描述如何在Bokeh中使用PyInstaller和Custom JS的帖子。但我真的需要这样做,因为我必须分发可执行文件。任何帮助都非常感谢,并提前感谢!!

我使用的是Pyinstaller 3.3.1,Python 2.7和Bokeh 0.12.11

4 个答案:

答案 0 :(得分:2)

所以,问题是程序找不到 compiler.js 文件。我不得不做一些更改,将此文件合并到PyInstaller可执行文件中。在 .. \ bokeh \ util 下,存在 compiler.py 文件。在 nodejs_compile 函数下的此文件中,

def nodejs_compile(code, lang="javascript", file=None):
    compilejs_script = join(bokehjs_dir, "js", "compiler.js")
    ...
    ...

它正在使用 bokehjs_dir ,它已在文件中定义为

bokehjs_dir = settings.bokehjsdir()

bokehjs_dir 是一个变量,最终被其他变量追加,并使程序的路径到达 compiler.js 文件)

因此 bokehjs_dir 的值被设置为 bokeh 目录的绝对路径,这导致了问题,因为生成的可执行文件无法访问该目录。

所以只需注释掉该行并添加代码段,如下所示,

import bokeh

#bokehjs_dir = settings.bokehjsdir()

if getattr(sys, 'frozen', False):
   # we are running in a bundle        
   temp_dir = sys._MEIPASS
else:
   # we are running in a normal Python environment
   temp_dir = os.path.dirname(bokeh.__file__)

bokehjs_dir = temp_dir + '\\server\\static' 

正如在已经引用的帖子中所提到的,它的作用是当代码被冻结时,它重定向程序以查看sys._MEIPASS(由PyInstaller创建的临时文件夹以解压缩包)。

现在需要做的就是在包中使用.. \ bokeh \ server \ static \ folder,其名称与 static 相同。这可以通过编辑pyinstaller .spec文件来完成。编辑过的.spec文件如下所示,

a = Analysis(['module1.py'],
         pathex=['C:\\Users\\user_name\\Desktop\\PythonFiles'],
         binaries=[],
         datas=[(r'C:\Python27\Lib\site-packages\bokeh\core\_templates', '_templates'), (r'C:\Python27\Lib\site-packages\bokeh\server', 'server')],
         hiddenimports=[],
         hookspath=[],
         runtime_hooks=[],
         excludes=[],
         win_no_prefer_redirects=False,
         win_private_assemblies=False,
         cipher=block_cipher)

通过编辑,我们确保所需的 compiler.js 捆绑在PyInstaller可执行文件中。我们已经确保在程序运行时 compiler.py 文件会找到它。

希望如果他/她将面临同样的问题,这将有助于未来的人!

答案 1 :(得分:0)

我不确定这是否可行。从CoffeeScript进行编译需要在运行时由Bokeh安装和运行coffee编译器,该编译器是一个单独的外部可执行文件。您可能在纯JavaScript中重写了CustomJS代码,但即便如此,我认为您还需要安装node可执行文件并运行它。 (但我并非100%确定。)如果有一些方法可以将pyinstaller打包并将这些附加程序捆绑到那个Bokeh可以使用它们,你可以使它工作。

修改:我认为node只需要完整的自定义扩展程序,而不是CustomJS,所以我建议将您的coffeescript转换为纯javascript绝对值得一试

否则,我知道预先编译JS代码的想法,以便它可以静态捆绑,而不需要任何外部运行时可执行文件,在一个不同的GH问题中浮出水面,但我不会&#39 ;我认为它是自己的问题。请随时提交GitHub功能请求问题,但可能无法解决一段时间。

答案 2 :(得分:0)

对我来说,以下解决方案有效:

  • 替换bokeh.core.templates中的以下行(位于&#34; bokeh \ core \ templates.py&#34;):

    from jinja2 import Environment, PackageLoader, Markup
    
    _env = Environment(loader=PackageLoader('bokeh.core', '_templates'))
    _env.filters['json'] = lambda obj: Markup(json.dumps(obj))
    

由:

    import sys
    import os
    directory = os.path.dirname(sys.executable)

    _env = Environment(loader=FileSystemLoader(os.path.join(directory, '_templates')))
    _env.filters['json'] = lambda obj: Markup(json.dumps(obj))
  • 将文件夹bokeh \ core_templates复制到您的冻结.exe应用程序所在的输出文件夹中。

总之,问题的解决方案只是将模板显式复制到某个位置,并告诉冻结的应用程序在哪里找到这些模板。

答案 3 :(得分:0)

我认为这个话题可以结束。在Bokeh 0.13中,“ core.templates.py”文件已被修改,现在在冻结脚本时以正确的方式包括模板:

def get_env():
    ''' Get the correct Jinja2 Environment, also for frozen scripts.
    '''
    if getattr(sys, 'frozen', False):
        templates_path = os.path.join(sys._MEIPASS, '_templates')
        return Environment(loader=FileSystemLoader(templates_path))
    else:
        return Environment(loader=PackageLoader('bokeh.core', '_templates'))


_env = get_env()
_env.filters['json'] = lambda obj: Markup(json.dumps(obj))