Flu*_*lux 6 python jinja2 flask zipapp
我已经将我的 Flask Web 应用程序打包到一个可执行的 Python 压缩档案 ( zipapp ) 中。我在加载模板时遇到问题。Flask/Jinja2 无法找到模板。
为了加载模板,我使用jinja2.FunctionLoader
了一个加载函数,该函数应该能够从可执行 zip 存档内部读取捆绑文件(在本例中为 Jinja 模板)(参考:python:可执行 zip 文件可以包含数据文件吗?)。然而,加载函数无法找到模板(参见:(1)
代码中),即使模板可以从加载函数外部读取(参见:(2)
代码中)。
这是目录结构:
??? src
??? __main__.py
??? templates
??? index.html
??? __init__.py # Empty file.
Run Code Online (Sandbox Code Playgroud)
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)
Run Code Online (Sandbox Code Playgroud)
为了生成可执行存档,我将所有依赖项安装到src/
(使用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'
Run Code Online (Sandbox Code Playgroud)
该错误是由(1)
代码中引起的。作为调试工作的一部分,我添加(2)
了检查是否可以在文件的全局范围内找到模板。令人惊讶的是,它成功地找到并读取了模板文件。
是什么解释了(1)
和之间的行为差异(2)
?更重要的是,我怎样才能让 Flask 在可执行的 Python zip 存档中找到与 Flask 应用程序捆绑在一起的 Jinja 模板?
(Python 版本:Linux 上的 3.6.8;Flask 版本:1.1.1)
jinja2.FunctionLoader(load_template)
正在寻找一个函数来将完整index.html
模板作为 unicode 字符串返回。根据jinja2 文档:
一个加载器,传递一个执行加载的函数。该函数接收模板的名称,并且必须返回带有模板源的 unicode 字符串、形式为 (source、filename、uptodatefunc) 的元组,如果模板不存在,则返回 None。
pkgutil.get_data('templates', name)
不返回 unicode 字符串,而是返回 bytes 对象。要解决此问题,您应该使用pkgutil.get_data('templates', name).decode('utf-8')
def load_template(name):
"""
Loads file from the templates folder and returns file contents as a string.
See jinja2.FunctionLoader docs.
"""
return pkgutil.get_data('templates', name).decode('utf-8')
Run Code Online (Sandbox Code Playgroud)
这意味着第 (2) 部分将正常工作,因为代码作为index.html
字节对象打印。Print 可以处理字节对象,它在控制台上看起来几乎与字符串相同。但是,第 (1) 部分中的代码将失败,因为它被馈送到jinja2.FunctionLoader
需要字符串的位置。第(1)部分对ValueError
我来说失败了。
我怀疑由于您的错误消息是 aFileNotFoundError
并作为文件调用myapp
,因此您帖子的该部分与您的应用程序不完全匹配。我在 Windows 10 和 Ubuntu Server 18.04 以及 Python 3.6 和 3.7 上准确复制了这些说明,除了需要使用decode
. 我偶尔会遇到PermissionErrors
需要我运行sudo python3 myapp
.
归档时间: |
|
查看次数: |
722 次 |
最近记录: |