我有一个使用Jinja2模板的python脚本,我试图使用Pyinstaller创建一个文件夹分发。
在Jinja,我让程序通过使用PackageLoader
类来理解模板的位置。下面的代码显示它指向templates
Python包下的pycorr
文件夹。
env = Environment(loader=PackageLoader('pycorr', 'templates'))
template = env.get_template('child_template.html')
这就是我的文件夹结构:
pycorr
| |
| + templates
| |
| + base.html
| + child.html
当我使用Pyinstaller将软件包编译到一个文件夹中时,我没有看到任何与Jinja2相关的警告/错误,并且我能够启动.exe文件。但是,当程序开始寻找Jinja2模板时,它会失败,并在控制台窗口中显示此错误消息:
Traceback (most recent call last):
...
File "C:\Users\ ... \out00-PYZ.pyz\pycorr.WriterToHTML", line 96, in htmlout_table
File "C:\Users\ ... \out00-PYZ.pyz\pycorr.WriterToHTML", line 13, in __init__
File "C:\Users\ ... \out00-PYZ.pyz\pycorr.WriterToHTML", line 48, in __set_template
File "C:\Users\ ... \out00-PYZ.pyz\jinja2.environment", line 791, in get_template
File "C:\Users\ ... \out00-PYZ.pyz\jinja2.environment", line 765, in _load_template
File "C:\Users\ ... \out00-PYZ.pyz\jinja2.loaders", line 113, in load
File "C:\Users\ ... \out00-PYZ.pyz\jinja2.loaders", line 224, in get_source
File "C:\Users\ ... \dist\OCA_CO~1\eggs\setuptools-14.3-py2.7.egg\pkg_resources\__init__.py", line 1572, in has_resource
return self._has(self._fn(self.module_path, resource_name))
File "C:\Users\ ... \dist\OCA_CO~1\eggs\setuptools-14.3-py2.7.egg\pkg_resources\__init__.py", line 1627, in _has
"Can't perform this operation for unregistered loader type"
NotImplementedError: Can't perform this operation for unregistered loader type
我不太了解错误消息,但我的猜测是Pyinstaller需要找到templates
文件夹。所以我在Pyinstaller .spec文件中添加了这些行:
a.datas += [('BASE', './pycorr/templates/base.html', 'DATA')]
a.datas += [('TABLE', './pycorr/templates/table_child.html', 'DATA')]
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=None,
upx=False,
name='OCA_correlation')
但它似乎并没有解决问题。
有人可以帮忙吗?我多次阅读Pyinstaller手册,但我无法弄清楚。
答案 0 :(得分:2)
当尝试使用代码从PyInstaller发行版中将Pandas DataFrame呈现为html时,遇到类似的Jinja2错误,
html = df.style.render()
我通过修改软件包加载程序指令解决了该问题。
在Pandas样式文件中:site-packages \ pandas \ io \ formats \ style.py
我替换了
loader = PackageLoader("pandas", "io/formats/templates")
使用
if getattr(sys, 'frozen', False):
# we are running in a bundle
bundle_dir = sys._MEIPASS
loader = FileSystemLoader(bundle_dir)
else:
loader = PackageLoader("pandas", "io/formats/templates")
以及文件顶部的相应导入
import sys
现在,如果程序被“冻结”,则加载程序将在bundle目录中查找模板。在这种情况下,最后一步是将模板添加到捆绑软件中。为此,我使用--add-data命令从命令行运行了PyInstaller。例如,类似于以下命令的命令将添加默认模板html.tpl,
pyinstaller --add-data PATH1\site-packages\pandas\io\formats\templates\html.tpl;. PATH2\Main.py
答案 1 :(得分:1)
在构建GUI使用pyinstaller时遇到了这个问题。我使用Jinja2来呈现报告并且模板没有加载,而是我收到了#34;未注册的加载器类型"错误也是如此。在线阅读和测试许多解决方案我终于得到了修复:必须使用FileSystemLoader而不是PackageLoader。还需要为FileSystemLoader提供文件路径。我的最终解决方案是here和here的信息组合。
以下提供了此解决方案的完整示例。我的代码在testjinjia2下,子目录模板中有模板:
testjinja2
| |
| + templates
| |
| + base.html
| + report.html
testreport.py
testreport.spec
在testreport.spec中:
# -*- mode: python -*-
block_cipher = None
a = Analysis(['E:\\testjinja2\\testreport.py'],
pathex=['E:\\testjinja2'],
binaries=[],
datas=[('E:\\testjinja2\\templates\\base.html', '.'),
('E:\\testjinja2\\templates\\report.css', '.'),
('E:\\testjinja2\\templates\\report.html', '.')],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
name='testreport',
debug=False,
strip=False,
upx=True,
console=True )
在testreport.py中,
import os
import sys
from jinja2 import Environment, PackageLoader, FileSystemLoader
def resource_path(relative_path, file_name):
""" Get absolute path to resource, works for both in IDE and for PyInstaller """
# PyInstaller creates a temp folder and stores path in sys._MEIPASS
# In IDE, the path is os.path.join(base_path, relative_path, file_name)
# Search in Dev path first, then MEIPASS
base_path = os.path.abspath(".")
dev_file_path = os.path.join(base_path, relative_path, file_name)
if os.path.exists(dev_file_path):
return dev_file_path
else:
base_path = sys._MEIPASS
file_path = os.path.join(base_path, file_name)
if not os.path.exists(file_path):
msg = "\nError finding resource in either {} or {}".format(dev_file_path, file_path)
print(msg)
return None
return file_path
class Report:
def main(self, output_html_file):
# template_loader = PackageLoader("report", "templates")
# --- PackageLoader returns unregistered loader problem, use FileSystemLoader instead
template_file_name = 'report.html'
template_file_path = resource_path('templates', template_file_name)
template_file_directory = os.path.dirname(template_file_path)
template_loader = FileSystemLoader(searchpath=template_file_directory)
env = Environment(loader=template_loader) # Jinja2 template environment
template = env.get_template(template_file_name)
report_content_placeholder = "This is my report content placeholder"
html = template.render(report_content= report_content_placeholder)
with open(output_html_file, 'w') as f:
f.write(html)
if __name__ == "__main__":
my_report = Report()
my_report.main("output.html")
需要方法resource_path,因为jinja模板文件的文件路径在我的IDE中是不同的,而从exe文件中提取的文件是不同的。
还有一些简单的模板文件可供您试用 base.html文件
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<style>
.centered {
text-align: center;
}
.centeredbr {
text-align: center;
page-break-before:always;
}
.underlined {
text-decoration: underline;
}
</style>
</head>
<body>
{% block body %}{% endblock %}
</body>
</html>
报告html
<!DOCTYPE html>
{% extends "base.html" %}
{% block body %}
<h1 class="centered underlined">Report Title</h1>
<h2 class="centeredbr">Chaper I</h2>
<p>{{ report_content }}</p>
{% endblock %}
我正在使用pyinstaller 3.2.1和Python 3.5.1 Anaconda Custom(64位)
答案 2 :(得分:0)
从@Uynix开始,我发现我必须再做几个步骤,使用cx_freeze为我的问题版本实现解决方案。我的第一个解决方案帖子让我知道是否需要更多细节。
总之,我不得不修改 C:\ ProgramData \ Anaconda3 \ PKGS \背景虚化-0.12.9-py36_0 \ LIB \站点包\散景\芯\ templates.py
原始文件(散景0.12.9):
''' Provide Jinja2 templates used by Bokeh to embed Bokeh models
(e.g. plots, widgets, layouts) in various ways.
.. bokeh-jinja:: bokeh.core.templates.AUTOLOAD_JS
.. bokeh-jinja:: bokeh.core.templates.AUTOLOAD_NB_JS
.. bokeh-jinja:: bokeh.core.templates.AUTOLOAD_TAG
.. bokeh-jinja:: bokeh.core.templates.CSS_RESOURCES
.. bokeh-jinja:: bokeh.core.templates.DOC_JS
.. bokeh-jinja:: bokeh.core.templates.FILE
.. bokeh-jinja:: bokeh.core.templates.JS_RESOURCES
.. bokeh-jinja:: bokeh.core.templates.NOTEBOOK_LOAD
.. bokeh-jinja:: bokeh.core.templates.NOTEBOOK_DIV
.. bokeh-jinja:: bokeh.core.templates.PLOT_DIV
.. bokeh-jinja:: bokeh.core.templates.SCRIPT_TAG
'''
from __future__ import absolute_import
import json
from jinja2 import Environment, PackageLoader, Markup
_env = Environment(loader=PackageLoader('bokeh.core', '_templates'))
_env.filters['json'] = lambda obj: Markup(json.dumps(obj))
JS_RESOURCES = _env.get_template("js_resources.html")
CSS_RESOURCES = _env.get_template("css_resources.html")
SCRIPT_TAG = _env.get_template("script_tag.html")
PLOT_DIV = _env.get_template("plot_div.html")
DOC_JS = _env.get_template("doc_js.js")
FILE = _env.get_template("file.html")
NOTEBOOK_LOAD = _env.get_template("notebook_load.html")
NOTEBOOK_DIV = _env.get_template("notebook_div.html")
AUTOLOAD_JS = _env.get_template("autoload_js.js")
AUTOLOAD_NB_JS = _env.get_template("autoload_nb_js.js")
AUTOLOAD_TAG = _env.get_template("autoload_tag.html")
我将问题追溯到了这一行:
JS_RESOURCES = _env.get_template("js_resources.html")
我发现,cx_freeze没有正确编译,抛出同样的错误:
File "C:\ProgramData\Anaconda3\lib\site-packages\bokeh\core\templates.py", line 27, in <module>
JS_RESOURCES = _env.get_template("js_resources.html")
File "C:\ProgramData\Anaconda3\lib\site-packages\jinja2\environment.py", line 830, in get_template
return self._load_template(name, self.make_globals(globals))
File "C:\ProgramData\Anaconda3\lib\site-packages\jinja2\environment.py", line 804, in _load_template
template = self.loader.load(self, name, globals)
File "C:\ProgramData\Anaconda3\lib\site-packages\jinja2\loaders.py", line 113, in load
source, filename, uptodate = self.get_source(environment, name)
File "C:\ProgramData\Anaconda3\lib\site-packages\jinja2\loaders.py", line 234, in get_source
if not self.provider.has_resource(p):
File "C:\ProgramData\Anaconda3\lib\site-packages\pkg_resources\__init__.py", line 1464, in has_resource
return self._has(self._fn(self.module_path, resource_name))
File "C:\ProgramData\Anaconda3\lib\site-packages\pkg_resources\__init__.py", line 1514, in _has
"Can't perform this operation for unregistered loader type"
NotImplementedError: Can't perform this operation for unregistered loader type
新的templates.py文件:
''' Provide Jinja2 templates used by Bokeh to embed Bokeh models
(e.g. plots, widgets, layouts) in various ways.
.. bokeh-jinja:: bokeh.core.templates.AUTOLOAD_JS
.. bokeh-jinja:: bokeh.core.templates.AUTOLOAD_NB_JS
.. bokeh-jinja:: bokeh.core.templates.AUTOLOAD_TAG
.. bokeh-jinja:: bokeh.core.templates.CSS_RESOURCES
.. bokeh-jinja:: bokeh.core.templates.DOC_JS
.. bokeh-jinja:: bokeh.core.templates.FILE
.. bokeh-jinja:: bokeh.core.templates.JS_RESOURCES
.. bokeh-jinja:: bokeh.core.templates.NOTEBOOK_LOAD
.. bokeh-jinja:: bokeh.core.templates.NOTEBOOK_DIV
.. bokeh-jinja:: bokeh.core.templates.PLOT_DIV
.. bokeh-jinja:: bokeh.core.templates.SCRIPT_TAG
'''
from __future__ import absolute_import
import json
import sys, os
import bokeh.core
# from jinja2 import Environment, PackageLoader, Markup
from jinja2 import Environment, Markup, FileSystemLoader
# add in from Uynix
def resource_path(relative_path, file_name):
""" Get absolute path to resource, works for both in IDE and for PyInstaller """
# PyInstaller creates a temp folder and stores path in sys._MEIPASS
# In IDE, the path is os.path.join(base_path, relative_path, file_name)
# Search in Dev path first, then MEIPASS
base_path = os.path.abspath(".")
dev_file_path = os.path.join(base_path, relative_path, file_name)
if os.path.exists(dev_file_path):
return dev_file_path
else:
base_path = sys._MEIPASS
file_path = os.path.join(base_path, file_name)
if not os.path.exists(file_path):
msg = "\nError finding resource in either {} or {}".format(dev_file_path, file_path)
print(msg)
return None
return file_path
""" my new code here
"""
_env = Environment(loader=FileSystemLoader(os.path.dirname(bokeh.core.__file__) +'\\_templates'))
""" end of my new code
"""
_env.filters['json'] = lambda obj: Markup(json.dumps(obj))
# this is where the errors start to happen! need to replace get_template!
JS_RESOURCES = _env.get_template("js_resources.html")
CSS_RESOURCES = _env.get_template("css_resources.html")
SCRIPT_TAG = _env.get_template("script_tag.html")
PLOT_DIV = _env.get_template("plot_div.html")
DOC_JS = _env.get_template("doc_js.js")
FILE = _env.get_template("file.html")
NOTEBOOK_LOAD = _env.get_template("notebook_load.html")
NOTEBOOK_DIV = _env.get_template("notebook_div.html")
AUTOLOAD_JS = _env.get_template("autoload_js.js")
AUTOLOAD_NB_JS = _env.get_template("autoload_nb_js.js")
AUTOLOAD_TAG = _env.get_template("autoload_tag.html")
然后再次运行cx_freeze等,这次散景现在有效!