使用pyinstaller构建的Flask应用程序无法呈现index.html

时间:2015-08-21 22:02:37

标签: python flask jinja2

我已经写了一个烧瓶应用程序,它工作得很好。我想将它作为可执行文件分发。 尝试使用pyinstaller flaskScript.py来做它 dist文件夹生成了。 进入dist文件夹并双击我的可执行文件flaskScript,它启动我的服务器。 在访问url时,localhost:9090会出现以下异常

jinja2.exceptions.TemplateNotFound

TemplateNotFound: index.html

Traceback (most recent call last)
File "/Users/george/Downloads/flaskApps/flaskScript/build/flaskScript/out00-PYZ.pyz/flask.app", line 1836, in __call__

File "/Users/george/Downloads/flaskApps/flaskScript/build/flaskScript/out00-PYZ.pyz/flask.app", line 1820, in wsgi_app

File "/Users/george/Downloads/flaskApps/flaskScript/build/flaskScript/out00-PYZ.pyz/flask.app", line 1403, in handle_exception

File "/Users/george/Downloads/flaskApps/flaskScript/build/flaskScript/out00-PYZ.pyz/flask.app", line 1817, in wsgi_app

File "/Users/george/Downloads/flaskApps/flaskScript/build/flaskScript/out00-PYZ.pyz/flask.app", line 1477, in full_dispatch_request

File "/Users/george/Downloads/flaskApps/flaskScript/build/flaskScript/out00-PYZ.pyz/flask.app", line 1381, in handle_user_exception

File "/Users/george/Downloads/flaskApps/flaskScript/build/flaskScript/out00-PYZ.pyz/flask.app", line 1475, in full_dispatch_request

File "/Users/george/Downloads/flaskApps/flaskScript/build/flaskScript/out00-PYZ.pyz/flask.app", line 1461, in dispatch_request

File "<string>", line 13, in index

File "/Users/george/Downloads/flaskApps/flaskScript/build/flaskScript/out00-PYZ.pyz/flask.templating", line 127, in render_template

File "/Users/george/Downloads/flaskApps/flaskScript/build/flaskScript/out00-PYZ.pyz/jinja2.environment", line 851, in get_or_select_template

File "/Users/george/Downloads/flaskApps/flaskScript/build/flaskScript/out00-PYZ.pyz/jinja2.environment", line 812, in get_template

File "/Users/george/Downloads/flaskApps/flaskScript/build/flaskScript/out00-PYZ.pyz/jinja2.environment", line 774, in _load_template

File "/Users/george/Downloads/flaskApps/flaskScript/build/flaskScript/out00-PYZ.pyz/flask.templating", line 64, in get_source

TemplateNotFound: index.html

虽然在执行python flaskScript.py

时它在开发设置中工作正常

6 个答案:

答案 0 :(得分:12)

这个问题现在有点老了,但是当把它打包成一个文件时,我遇到了同样的问题(Python 3.4.3,Flask 0.10.1和PyInstaller 3.1.1)。

我设法通过在初始化脚本(app\__init__.py)中添加以下内容来解决它:

import sys
import os
from flask import Flask
# Other imports

if getattr(sys, 'frozen', False):
    template_folder = os.path.join(sys._MEIPASS, 'templates')
    app = Flask(__name__, template_folder=template_folder)
else:
    app = Flask(__name__)

# etc

问题是,当站点以打包的形式运行时,模板位于temp目录下名为 _MEIxxxxxx 的目录中(参见PyInstaller手册中的this),所以我们有告诉Flask。

这是通过template_folder参数完成的(我在这个答案hereAPI docs中稍后发现)。

最后,if确保我们在开发它时仍然可以使用它解压缩。如果是frozen,则打包脚本,我们必须告诉Flask在哪里找到模板;否则我们在Python环境中运行它(取自here)并且默认值将起作用(假设您当然使用标准的templates目录。)

答案 1 :(得分:2)

if getattr(sys, 'frozen', False):                                                                                                                                     
      template_folder = os.path.join(sys.executable, '..','templates')                                                                                                  
      static_folder = os.path.join(sys.executable, '..','static')                                                                                                       
      app = Flask(__name__, template_folder = template_folder,\                                                                                                         
                              static_folder = static_folder)

复制此代码并粘贴到您的文件中。现在pyinstaller在你的dist目录中查找静态和模板文件夹。现在复制并粘贴dist文件夹中的static和template文件夹。它会起作用

答案 2 :(得分:2)

如果您尝试创建--onefile executable ,您还需要在spec文件中添加目录。

  1. 在Python代码中,找到运行应用程序的位置并将路径存储在base_dir中:

    import os, sys
    base_dir = '.'
    if hasattr(sys, '_MEIPASS'):
        base_dir = os.path.join(sys._MEIPASS)
    
  2. 使用`static_folder和template_folder参数将正确的路径传递给Flask应用程序:

    app = Flask(__name__,
            static_folder=os.path.join(base_dir, 'static'),
            template_folder=os.path.join(base_dir, 'templates'))
    
  3. 在spec文件中,我们需要告诉pyinstaller包含templatesstatic文件夹,包括pyinstaller的 Analysis 部分中的相应文件夹:

    a = Analysis(
         ...
         datas=[
           ('PATH_TO_THE_APP\\static\\', 'static'),
           ('PATH_TO_THE_APP\\templates\\', 'templates'),
         ],
         ...
    
  4. 一点解释:

    使用pyinstaller打包后未找到文件的一般问题是文件将更改其路径。使用--onefile选项,您的文件将在exe中压缩。当你执行exe时,它们是未压缩的并放在某个临时文件夹中。

    每次执行文件时某处都会发生变化,但是运行的应用程序(不是spec文件,而是主要的python文件,比如说main.py)可以在这里找到:

    import os
    os.path.join(sys._MEIPASS)
    

    因此,模板文件不会出现在./templates中,而是出现在os.path.join(os.path.join(sys._MEIPASS), templates)中。现在的问题是,除非打包,否则您的代码将无法运行。因此,python main.py不会运行。

    因此,需要在正确的位置找到文件的条件:

    import os, sys
    base_dir = '.'
    if hasattr(sys, '_MEIPASS'): # or, untested: if getattr(sys, 'frozen', False):
        base_dir = os.path.join(sys._MEIPASS)
    

    以下是有关runtime information in pyinstaller

    的更多信息

答案 3 :(得分:1)

我刚刚写了一篇关于这个问题的博客文章和其他类似的问题, Create one executable file for a Flask app with PyInstaller

基本上优雅的解决方案是执行以下操作:

pyinstaller -w -F --add-data "templates;templates" --add-data "static;static" app.py

Linux(未测试)

pyinstaller -w -F --add-data "templates:templates" --add-data "static:static" app.py

答案 4 :(得分:0)

如果您是从.spec文件构建的,则无需更改即可轻松实现

__int__.py

1)将模板和静态文件夹添加到.spec中的数据

2)确保将jinja2添加到hiddenimports

block_cipher = None

# add templates and static folders to a list
added_files =[
    ('templates/*.html', 'templates'),
    ('static/*.css', 'static'),
    ]

a = Analysis(['run.py'],
             pathex=['/your/app/location'],
             binaries=[],
             datas = added_files, # set datas = added_files list
             hiddenimports=['jinja2.ext'], # be sure to add jinja2 
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher,
             noarchive=False)
             ...

此方法直接来自pyinstaller文档here

答案 5 :(得分:0)

pyinstaller spec_an_API.py --onefile --add-data "templates";"templates" --add-data "static";"static"

对于使用 --onefile 的窗口