我已将 Flask Web 应用程序打包成可执行的 Python 压缩存档(zipapp https://docs.python.org/3.6/library/zipapp.html)。我在加载模板时遇到问题。 Flask/Jinja2 无法找到模板。
为了加载模板,我使用了jinja2.FunctionLoader
具有加载函数,该函数应该能够从可执行 zip 存档中读取捆绑文件(在本例中为 Jinja 模板)(参考:python:可执行zip文件可以包含数据文件吗? https://stackoverflow.com/questions/5355694/python-can-executable-zip-files-include-data-files)。但是,加载函数无法找到模板(请参阅:(1)
在代码中),即使模板可以从加载函数外部读取(请参阅:(2)
在代码中)。
这是目录结构:
└── src
├── __main__.py
└── templates
├── index.html
└── __init__.py # Empty file.
src/__main__.py
:
import pkgutil
import jinja2
from flask import Flask, render_template
def load_template(name):
# (1) ATTENTION: this produces an error. Why?
# Error message:
# FileNotFoundError: [Errno 2] No such file or directory: 'myapp'
data = pkgutil.get_data('templates', name)
return data
# (2) ATTENTION: Unlike (1), this successfully found and read the template file. Why?
data = pkgutil.get_data('templates', 'index.html')
print(data)
# This also works:
data = load_template('index.html')
print(data)
# Why?
app = Flask(__name__)
app.config['SECRET_KEY'] = 'my-secret-key'
app.jinja_loader = jinja2.FunctionLoader(load_template) # <-
@app.route('/')
def index():
return render_template('index.html')
app.run(host='127.0.0.1', port=3000)
为了生成可执行存档,我将所有依赖项安装到src/
(using pip3 install wheel flask --target src/
),然后我跑了python3 -m zipapp src/ -o myapp
生成可执行存档本身。然后我使用运行可执行存档python3 myapp
。不幸的是,尝试通过网络浏览器访问索引页会导致错误:
# ...
File "myapp/__main__.py", line 10, in load_template
File "/usr/lib/python3.6/pkgutil.py", line 634, in get_data
return loader.get_data(resource_name)
FileNotFoundError: [Errno 2] No such file or directory: 'myapp'
该错误是由以下原因引起的(1)
在代码中。作为调试工作的一部分,我添加了(2)
检查是否可以在文件的全局范围内找到模板。令人惊讶的是,它成功找到并读取了模板文件。
是什么导致了之间的行为差异(1)
and (2)
?更重要的是,如何让 Flask 找到与可执行 Python zip 存档内的 Flask 应用程序捆绑在一起的 Jinja 模板?
(Python 版本:Linux 上的 3.6.8;Flask 版本:1.1.1)