Search code examples
pythontemplatesflaskjinja2pyinstaller

Pyinstaller Jinja2 TemplateNotFound


I am using pyinstaller to build my flask application, everything is working fine except I get problems with Jinja2 templates.

It gave me jinja2.exceptions.TemplateNotFound,

I tried to put from app import template which is the templates folder, but it didn't work (I guess since they don't contain any py file).

I also tried changing the .spec file to include the templates folder

added_files = [
         ( '..\\CommerceApp\\app\\templates', 'templates' ),
         ( '..\\CommerceApp\\app\\static', 'static' )
        ]

a = Analysis(['..\\CommerceApp\\run.py'],
             pathex=['D:\\PythonProjects\\CommerceAppExe'],
             binaries=None,
             datas=added_files,
             hiddenimports=[],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher)

But it didn't work either, same result as if I copy the folder manually by myself.

Is there any way to include Template bundled together with the .exe?


Edit

This is my spec file

# -*- mode: python -*-

block_cipher = None

a = Analysis(['..\\CommerceApp_withPyInstaller\\run.py'],
             pathex=['D:\\PythonProjects\\CommerceAppExe'],
             binaries=None,
             datas=[],
             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,
          exclude_binaries=True,
          name='SupplyTracker',
          debug=False,
          strip=False,
          upx=True,
          console=True )
coll = COLLECT(exe,
               a.binaries,
               a.zipfiles,
               a.datas,
               strip=False,
               upx=True,
               name='SupplyTracker')

Edit 2

Accepted Answer changed to gmas80 because it fixes the problem.

Edit 3

Also I just realize, I can just make a new folder with my package name and fill in the static templates css, html, etc, and it is gonna work (similar result from what gmas80 script does)


Solution

  • I don't believe that the issue is what is described in https://stackoverflow.com/a/35816876/2741329. I have just been able to freeze an application with Jinja2.

    In my spec file I use this approach to collect all the templates:

    from PyInstaller.building.build_main import Analysis, PYZ, EXE, COLLECT, BUNDLE, TOC
    
    
    def collect_pkg_data(package, include_py_files=False, subdir=None):
        import os
        from PyInstaller.utils.hooks import get_package_paths, remove_prefix, PY_IGNORE_EXTENSIONS
    
        # Accept only strings as packages.
        if type(package) is not str:
            raise ValueError
    
        pkg_base, pkg_dir = get_package_paths(package)
        if subdir:
            pkg_dir = os.path.join(pkg_dir, subdir)
        # Walk through all file in the given package, looking for data files.
        data_toc = TOC()
        for dir_path, dir_names, files in os.walk(pkg_dir):
            for f in files:
                extension = os.path.splitext(f)[1]
                if include_py_files or (extension not in PY_IGNORE_EXTENSIONS):
                    source_file = os.path.join(dir_path, f)
                    dest_folder = remove_prefix(dir_path, os.path.dirname(pkg_base) + os.sep)
                    dest_file = os.path.join(dest_folder, f)
                    data_toc.append((dest_file, source_file, 'DATA'))
    
        return data_toc
    
    pkg_data = collect_pkg_data('<YOUR LIB HERE>')
    

    Then add pkg_data to the COLLECT (1-folder) or to the EXE (1-file) .spec.

    In the 1-folder solution, you should be able to find all your templates in the created sub-folder.


    Edit

    This might work (assuming that you have a package (i.e., you have an __init__.py) following these suggestions: http://flask.pocoo.org/docs/0.10/patterns/packages/):

    # -*- mode: python -*-
    
    # <<< START ADDED PART    
    from PyInstaller.building.build_main import Analysis, PYZ, EXE, COLLECT, BUNDLE, TOC
    
    
    def collect_pkg_data(package, include_py_files=False, subdir=None):
        import os
        from PyInstaller.utils.hooks import get_package_paths, remove_prefix, PY_IGNORE_EXTENSIONS
    
        # Accept only strings as packages.
        if type(package) is not str:
            raise ValueError
    
        pkg_base, pkg_dir = get_package_paths(package)
        if subdir:
            pkg_dir = os.path.join(pkg_dir, subdir)
        # Walk through all file in the given package, looking for data files.
        data_toc = TOC()
        for dir_path, dir_names, files in os.walk(pkg_dir):
            for f in files:
                extension = os.path.splitext(f)[1]
                if include_py_files or (extension not in PY_IGNORE_EXTENSIONS):
                    source_file = os.path.join(dir_path, f)
                    dest_folder = remove_prefix(dir_path, os.path.dirname(pkg_base) + os.sep)
                    dest_file = os.path.join(dest_folder, f)
                    data_toc.append((dest_file, source_file, 'DATA'))
    
        return data_toc
    
    pkg_data = collect_pkg_data('<yourapplication>')  # <<< Put the name of your package here
    # <<< END ADDED PART    
    
    block_cipher = None
    
    a = Analysis(['..\\CommerceApp_withPyInstaller\\run.py'],
                 pathex=['D:\\PythonProjects\\CommerceAppExe'],
                 binaries=None,
                 datas=[],
                 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,
              exclude_binaries=True,
              name='SupplyTracker',
              debug=False,
              strip=False,
              upx=True,
              console=True )
    coll = COLLECT(exe,
                   a.binaries,
                   a.zipfiles,
                   a.datas,
                   pkg_data,  # <<< Add here the collected files
                   strip=False,
                   upx=True,
                   name='SupplyTracker')