无法将Jinja2模板包含到Pyinstaller发行版中

2024-10-01 09:40:45 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个使用Jinja2模板的python脚本,并尝试使用Pyinstaller创建一个单文件夹分发版。在

在Jinja中,我让程序通过使用PackageLoader类来理解模板的位置。下面的代码显示它指向的是pycorrPython包下的templates文件夹。在

env = Environment(loader=PackageLoader('pycorr', 'templates'))
template = env.get_template('child_template.html')

下面是我的文件夹结构:

^{pr2}$

当我使用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手册读了好几遍,但就是搞不懂。在


Tags: in文件夹模板jinja2linetemplateuserstemplates
3条回答

从@Uynix开始,我发现我必须执行更多的步骤来使用cx_freeze实现我版本的问题解决方案。我的第一个解决方案帖子,让我知道如果需要更多的细节。在

总之,我不得不修改 C: \ProgramData\Anaconda3\pkgs\bokeh-0.12.9-py36_0\Lib\site packages\bokeh\core\模板.py在

原始文件(bokeh 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")

我把问题追根溯源:

^{pr2}$

我发现,由于某种原因,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

新的模板.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等,这一次bokeh现在工作了!在

我在尝试使用代码从PyInstaller发行版中将Pandas数据帧呈现为html时遇到了类似的Jinja2错误

html = df.style.render()

我通过修改包加载器指令解决了这个问题。在

在Pandas样式文件中:site packages\Pandas\io\formats\样式.py在

我换了

^{pr2}$

有了

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目录中查找模板。在这种情况下,最后一步是将模板添加到包中。为此,我使用adddata命令从命令行运行PyInstaller。例如,类似下面的命令将添加默认模板html.tpl文件你说

pyinstaller  add-data PATH1\site-packages\pandas\io\formats\templates\html.tpl;.  PATH2\Main.py

我在使用pyinstaller构建GUI时遇到了这个问题。我使用Jinja2呈现一个报表,模板没有加载,相反,我也收到了“unregistered loader type”错误。通过在线阅读和测试许多解决方案,我终于找到了一个解决方案:必须使用FileSystemLoader而不是PackageLoader。还需要为FileSystemLoader提供文件路径。我的最终解决方案是来自here和{a2}的信息的组合。在

下面提供了此解决方案的完整示例。我的代码在testjinjia2下,模板在子目录templates中:

testjinja2
| |
| + templates
|    |
|    + base.html
|    + report.html
testreport.py
testreport.spec

在测试报告.spec公司名称:

^{pr2}$

在测试报告.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")

需要一个方法资源_路径,因为jinja模板文件的文件路径在我的IDE和从exe文件中提取的文件不同。在

还有一些简单的模板文件供您试用。
基本.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和python3.5.1anaconda自定义(64位)

相关问题 更多 >