如何使 Flask/Jinja2 加载可执行 zip 存档中的捆绑模板?

2023-12-28

我已将 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)


jinja2.FunctionLoader(load_template)正在寻找一个函数来返回完整的index.html作为 unicode 字符串的模板。根据jinja2 文档 https://jinja.palletsprojects.com/en/2.11.x/api/#jinja2.FunctionLoader:

一个加载器,传递一个执行加载的函数。该函数接收模板的名称,并且必须返回带有模板源的 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') 

这意味着第 (2) 部分将正常工作,因为代码正在打印index.html作为字节对象。 Print 可以处理字节对象,它在控制台上看起来几乎与字符串相同。但是,第 (1) 部分中的代码将失败,因为它被馈送到jinja2.FunctionLoader它需要一个字符串。第 (1) 部分失败,原因是ValueError为我。

我怀疑,因为你的错误消息是FileNotFoundError并大声喊道myapp作为文件,您帖子的这一部分与您的申请不完全匹配。我在 Windows 10 和 Ubuntu Server 18.04 以及 Python 3.6 和 3.7 上准确复制了这些说明,除了需要使用之外没有任何问题decode。我偶尔会遇到PermissionErrors在 Ubuntu 上需要我运行sudo python3 myapp.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使 Flask/Jinja2 加载可执行 zip 存档中的捆绑模板? 的相关文章

随机推荐

  • Ajax 就绪状态 3(Chrome / IE)

    我正在和一些人玩comet and mxhr技术 事实证明 Chrome 5 实际上会发射一个readystate 3 但是responseText总是empty there 直到今天我还以为只是InternetExplorers 我这里错
  • 高性能缓存

    以下代码应该缓存上次读取的内容 这LastValueCache是一个可以被许多线程访问的缓存 这就是我使用共享内存的原因 对我来说 有竞争条件是可以的 但我希望其他线程能够看到更改LastValueCache class Repositor
  • Java日历日期错误

    谁能帮我理解为什么我得到不同的月份值 SimpleDateFormat dateFormat new SimpleDateFormat dd MM yyyy System out println dateFormat format cal
  • 如何加快从mysql到neo4j的插入速度?

    我在 mysql 中有一个包含 60000 个项目的数据集 我正在尝试将其插入 Neo4j 中 插入正在进行 但需要很长时间 大约每 3 秒 10 15 次 有什么办法可以加快速度吗 还有什么方法可以让我在 Neo4j 中提供诸如唯一键之类
  • 在 postgresql 中删除停用词而不进行词干化

    我想从数据中删除停用词 但我不想阻止这些词 因为确切的词对我很重要 我用了这个查询 SELECT to tsvector english colName from tblName order by lower asc 有什么方法可以在不阻止
  • 实体框架 6:将子对象添加到父对象的列表与将子对象的导航属性设置为父对象

    我有一个包含两个表的现有数据库MailServers and MailDomains in it MailDomains有外键列MailServerId指向Id主键列在MailServers 所以我们这里有一对多的关系 我跟着本文 http
  • Python:使用 setproctitle 更改进程名称

    我有一个 python 脚本 它启动许多 C 程序 每个程序都会传递一个命令行参数 如下所示 process path test process name test num process 10 for p in range 1 num p
  • Sequelize:如何在使用左外连接的连接表上执行 WHERE 条件

    我的数据库模型如下 员工驾驶一辆或零辆车辆一辆车可以由一名或多名员工驾驶车辆有一个模型类型 可以告诉我们它的燃料类型以及其他信息 我想要续集为我找到所有不开车的员工 或者如果他们开车 那么车辆不是柴油车 因此 其中 VehicleID 为
  • 带有 SharePoint 参数的 VB.Net 命令行(控制台)程序

    我想在 VB net 中创建一个允许参数的控制台程序 我想要做的是在下面的代码中添加参数 以便可以从 运行 菜单创建 Web 部件页面 例如C MyProgram exe Design 这将创建 Design Webpart 页面 我尝试在
  • 删除图中的文本

    我正在使用绘图功能sizetree from library plotrix 版本 3 8 1 这个函数有一个showcount允许括号中的一些计数显示在绘图上的参数 见下图 但我想知道为什么当我使用showcount FALSE 它们周围
  • Python 游戏网络

    我目前在寻找网络游戏编程资源时遇到困难 特别是Python 我不知道任何其他语言 我在 Python 中发现了很多关于通用网络的东西 但我不确定这就是我需要的 因为我相信游戏网络还涉及一些其他因素 我正在尝试创建一个在不同计算机上玩的 2
  • Python。如何使用libxml2获取属性值

    我使用的是 MINIDOM 但它不提供 xpath 方法 我现在尝试使用 libxml2 但在检索属性值时遇到问题 我的 xml 摘录如下
  • jersey 2.3.1 和 spring 集成兼容性问题

    我正在尝试创建将使用球衣和弹簧的宁静服务项目设置 我最初下载了 jersey1 8 依赖的 jar 我还得到了 jersey spring 1 8 并且我使用 com sun jersey spi spring container serv
  • 如何使用一对 FrameLabels 制作绘图网格?

    创建行 列 网格图 整个网格具有单个 FrameLabel 的最简单方法是什么 我需要类似的东西 p ListPlot RandomInteger 10 5 Joined gt True Axes gt False Frame gt Tru
  • Google 地图 API 3 搜索框

    我不知道如何在我的谷歌地图中实现搜索框 我有它 用户可以从表单中选择一些内容 然后在地图上加载标记 现在我想添加他们可以使用谷歌搜索框输入城市和州的位置 例如在maps google com上 这可以通过 API v 3 来完成吗 Goog
  • Eclipse:选择不包含任何可以在服务器上运行的资源

    我无法将 Maven Java Web 应用程序项目运行到 Eclipse IDE 中配置的 Tomcat 最初 我可以右键单击该项目并在 tomcat 服务器上运行它 但自从我将项目共享到存储库后 我无法执行此操作 我从存储库中断开了项目
  • 确定 JS AudioContext.analysisrNode 中的频率

    背景 我的目标是创建一个基于 JavaScript 的 Web 应用程序来分析和显示音频源 包括页内源 中的频率信息
  • 当理论规定使用已检查异常时,我是否应该使用相关的内置未检查异常?

    SO 上有很多关于 检查与非检查异常 主题的帖子 这个答案 https stackoverflow com a 19061110 2520359可能是最全面 信息最丰富的 然而 我仍然对遵循那里提出的逻辑感到矛盾 这是有原因的 我正在围绕一
  • 我应该在我的应用程序中包含命令行模式吗?

    出于学习目的 我正在 C 和 winforms 中开发一个类生成应用程序 我认为包含允许在脚本中使用应用程序的命令行模式可能会很好 在我的应用程序中包含命令行模式是一个很好的做法吗 最好有两个不同的程序 一个带有 GUI 一个用于命令行 实
  • 如何使 Flask/Jinja2 加载可执行 zip 存档中的捆绑模板?

    我已将 Flask Web 应用程序打包成可执行的 Python 压缩存档 zipapp https docs python org 3 6 library zipapp html 我在加载模板时遇到问题 Flask Jinja2 无法找到