2015-07-07 10 views
5

Mam skrypt Pythona, który używa szablonu Jinja2, i próbuję utworzyć dystrybucję jednego folderu za pomocą Pyinstaller.Nie można dołączyć szablonu Jinja2 do dystrybucji Pyinstaller

W języku Jinja pozwolę programowi zrozumieć położenie szablonów przy użyciu klasy PackageLoader. Poniższy kod pokazuje, że wskazuje na mój folder templates pod pakietem Python pycorr.

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

A oto co moja struktura folderów wygląda następująco:

pycorr 
| | 
| + templates 
| | 
| + base.html 
| + child.html 

Kiedy skompilować pakiet w jednym folderze, używając Pyinstaller, nie widzę żadnego ostrzeżenia/błąd związany Jinja2, a ja "Jestem w stanie uruchomić plik .exe. Jednak, gdy program zacząć szukać Jinja2 szablon, nie jest on z tego komunikatu o błędzie wyświetlanego w oknie konsoli:

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 

ja naprawdę nie rozumiem komunikat o błędzie, ale wydaje mi się, że Pyinstaller trzeba znaleźć templates folder. Więc dodałem te linie w .spec pliku Pyinstaller:

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') 

Ale to nie wydaje się, aby rozwiązać ten problem.

Czy ktoś może pomóc? Przeczytałem instrukcję PyCryptera kilka razy, ale po prostu nie mogę tego rozgryźć.

+1

Mimo, że to pytanie jest dość stare, a autor mógł to sobie wyobrazić, 'pkg_resources' nie jest obsługiwane przez program usuwający pismo, jak podano [tutaj] (https://github.com/pyinstaller/pyinstaller/wiki/FAQ#misc). Dlatego nie można użyć programu ładującego pakiety Jinja2. – jrast

Odpowiedz

1

Wpadłem na ten problem podczas budowania GUI za pomocą Pyinstallera. Użyłem Jinja2 do renderowania raportu i szablony nie ładowały się, zamiast tego otrzymałem również błąd "niezarejestrowany typ ładowarek". Czytanie i testowanie wielu rozwiązań w Internecie W końcu miałem naprawę: FileSystemLoader musi być używany zamiast PackageLoader. Dla FileSystemLoader należy podać ścieżkę do pliku. Ostatecznym rozwiązaniem jest połączenie informacji z here i here.

Poniżej znajduje się pełny przykład tego rozwiązania. Moje kodu jest pod testjinjia2 z szablonów w szablonach podkatalogu:

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

W 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) 

W 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") 

Sposób resource_path jest potrzebna, ponieważ Ścieżka do pliku szablonów jinja różni się w moim IDE i wyodrębnionych plikach z pliku exe.

Również niektóre proste pliki szablonów do wypróbowania.
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> 

Zgłoś 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 %} 

używam pyinstaller 3.2.1 i Python 3.5.1 Anaconda Custom (64-bit)

0

Idąc z @Uynix, stwierdziłem, że muszę wykonać kilka dodatkowych kroków, aby zaimplementować rozwiązanie dla mojej wersji problemu za pomocą cx_freeze. Moje pierwsze stanowisko dotyczące rozwiązania, daj mi znać, jeśli potrzebujesz więcej informacji.

Podsumowując, musiałem zmodyfikować C: \ ProgramData \ Anaconda3 \ pkgs \ bokeh-0.12.9-py36_0 \ Lib \ site-packages \ bokeh \ rdzeń \ templates.py

Plik Original (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") 

Wywiodłem problemu na linii:

JS_RESOURCES = _env.get_template("js_resources.html") 

Który odkryłem, jakoś nie był skompilowany poprawnie cx_freeze, rzuca ten sam błąd:

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 

Nowy plik 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") 

Potem pobiegł cx_freeze itp ponownie i tym razem bokeh działa teraz!