我正在开发一个使用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
答案 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))
总之,问题的解决方案只是将模板显式复制到某个位置,并告诉冻结的应用程序在哪里找到这些模板。
答案 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))