PiBer2
PiBer2

Reputation: 169

Flask app packaged with PyInstaller with no templates nor static included in EXE

I´m trying to pack my run.py app into run.exe, but not including html templates and the static folder files in the .exe file, so they can be easily changed and updated without the need to recompile the .exe file.

The app structure is:

 run.py
   /serversystem
      /templates
      /static
      /main
      /users

In "main" and "users" folders there are empty __init__.py files, and routes.py files with the actual code, in a Blueprint architecture.

I am using pyinstaller --onefile run.py and copying the packaged dist\run.exe file to the root folder where run.py lives.

The error I get is that Jinja cannot find the template when I tell it to render it. This is from http://localhost:3000/about:

Traceback (most recent call last):
  File "C:\Users\GP\AppData\Local\Temp\_MEI171402\flask\app.py", line 2091, in __call__
    
  File "C:\Users\GP\AppData\Local\Temp\_MEI171402\flask\app.py", line 2076, in wsgi_app
    
  File "C:\Users\GP\AppData\Local\Temp\_MEI171402\flask\app.py", line 2073, in wsgi_app
    
  File "C:\Users\GP\AppData\Local\Temp\_MEI171402\flask\app.py", line 1518, in full_dispatch_request
    
  File "C:\Users\GP\AppData\Local\Temp\_MEI171402\flask\app.py", line 1516, in full_dispatch_request
    
  File "C:\Users\GP\AppData\Local\Temp\_MEI171402\flask\app.py", line 1502, in dispatch_request
    
  File "C:\Users\GP\AppData\Local\Temp\_MEI171402\serversystem\main\routes.py", line 17, in about
    
  File "C:\Users\GP\AppData\Local\Temp\_MEI171402\flask\templating.py", line 148, in render_template
    
  File "C:\Users\GP\AppData\Local\Temp\_MEI171402\jinja2\environment.py", line 1068, in get_or_select_template
    
  File "C:\Users\GP\AppData\Local\Temp\_MEI171402\jinja2\environment.py", line 997, in get_template
    
  File "C:\Users\GP\AppData\Local\Temp\_MEI171402\jinja2\environment.py", line 958, in _load_template
    
  File "C:\Users\GP\AppData\Local\Temp\_MEI171402\jinja2\loaders.py", line 125, in load
    
  File "C:\Users\GP\AppData\Local\Temp\_MEI171402\flask\templating.py", line 59, in get_source
    
  File "C:\Users\GP\AppData\Local\Temp\_MEI171402\flask\templating.py", line 95, in _get_source_fast
    
jinja2.exceptions.TemplateNotFound: about.html

The same code works well in the IDE.

I have added a chunk of code to /systemserver/__init__.py that goes:

def create_app(config_class=Config):
 if getattr(sys, 'frozen', False):
     print("Starting in FROZEN Mode")
 else:
     print("Starting in NORMAL Mode")

 app = Flask(__name__)

 print("Instance Path   = {}".format(app.instance_path))
 print("Root Path       = {}".format(app.root_path))
 print("Templates Path  = {}".format(app.template_folder))
 print("Static Path     = {}".format(app.static_folder))
 print("Static URL Path = {}".format(app._static_url_path))

When running from IDE (VSCode), I get this:

Starting in NORMAL Mode
Instance Path   = h:\MyProject\Alfa1D\instance
Root Path       = h:\MyProject\Alfa1D\serversystem
Templates Path  = templates
Static Path     = h:\MyProject\Alfa1D\serversystem\static
Static URL Path = None

When running from exe file, this is the output:

Starting in FROZEN Mode
Instance Path   = C:\Users\GP\AppData\Local\Temp\_MEI144962\var\serversystem-instance
Root Path       = C:\Users\GP\AppData\Local\Temp\_MEI144962\serversystem
Templates Path  = templates
Static Path     = C:\Users\GP\AppData\Local\Temp\_MEI144962\serversystem\static
Static URL Path = None

As I haven´t used the template_folder=template_folder, static_folder=static_folder options when creating the Flask app, the template and static files do not get packed so they don´t go to the temp MEI folder.

Is it possible to pack the EXE file and, when running it from where run.py is, make it use the template & static files from the fixed folders and not the temp one?

I use Python 3.10 and PyInstaller 4.7, on Windows 10.

Upvotes: 2

Views: 2283

Answers (1)

PiBer2
PiBer2

Reputation: 169

Ok, the following approach worked out well with the --onefile PyInstaller switch:

templatesDir = os.getcwd() + '/serversystem/templates'
staticDir = os.getcwd() + '/serversystem/static'

app = Flask(__name__, template_folder=templatesDir, static_folder=staticDir)

As I´m running my main script from a folder that has a "serversystem" folder, where "templates" and "static" in turn are located, that´s my way to get it. If you have templates and static in direct folders, you should use os.getcwd() + '/templates' and the same for '/static'.

This worked great in both normal (Python interpreter) and frozen (Pyinstaller packaged) modes.

This reference helped a lot, specially the little sample script at the end:

https://pyinstaller.readthedocs.io/en/stable/runtime-information.html

Upvotes: 2

Related Questions