I have packaged my Flask web application into a executable Python zipped archive (zipapp). I am having problems with the loading of templates. Flask/Jinja2 is unable to find the templates using
jinja2.FunctionLoader with a loading function that should have been able to read bundled files (in this case, Jinja templates) from inside the executable zip archive (reference: python: can executable zip files include data files?). However, the loading function is unable to find the template (see:
(1) in the code), even though the template is readable from outside the loading function (see:
(2) in the code).
Here’s the directory structure:
└── src ├── __main__.py └── templates ├── index.html └── __init__.py # Empty file.
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) 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)
To produce the executable archive, I installed all dependencies into
pip3 install wheel flask --target src/), then I ran
python3 -m zipapp src/ -o myapp to produce the executable archive itself. I then ran the executable archive using
python3 myapp. Unfortunately, trying to access the the index page through a web browser results in an error:
# ... 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'
The error is caused by
(1) in the code. As part of my debugging effort, I added
(2) to check whether or not the template could be found in the global scope of the file. Surprisingly, it successfully finds and reads the template file.
What accounts for the difference in behavior between
(2)? More importantly, how can I make Flask find Jinja templates that are bundled together with a Flask app inside an executable Python zip archive?
(Python version: 3.6.8 on linux; Flask version: 1.1.1)