Home » Python » python – How to make Flask/Jinja2 load bundled templates in an executable zip archive?-Exceptionshub

python – How to make Flask/Jinja2 load bundled templates in an executable zip archive?-Exceptionshub

Posted by: admin February 24, 2020 Leave a comment

Questions:

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.

I used 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.

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)

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 src/ (using 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 (1) and (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)

How to&Answers: