George
George

Reputation: 223

Flask application built using pyinstaller not rendering index.html

I have written a flask application and it works perfectly fine. I wanted to distribute it as an executable. Tried doing it using pyinstaller flaskScript.py dist folder got generated. Went into the dist folder and double clicked my executable flaskScript, it starts my server. On accessing the url, localhost:9090 it gives the following exception

jinja2.exceptions.TemplateNotFound

TemplateNotFound: index.html

Traceback (most recent call last)
File "/Users/george/Downloads/flaskApps/flaskScript/build/flaskScript/out00-PYZ.pyz/flask.app", line 1836, in __call__

File "/Users/george/Downloads/flaskApps/flaskScript/build/flaskScript/out00-PYZ.pyz/flask.app", line 1820, in wsgi_app

File "/Users/george/Downloads/flaskApps/flaskScript/build/flaskScript/out00-PYZ.pyz/flask.app", line 1403, in handle_exception

File "/Users/george/Downloads/flaskApps/flaskScript/build/flaskScript/out00-PYZ.pyz/flask.app", line 1817, in wsgi_app

File "/Users/george/Downloads/flaskApps/flaskScript/build/flaskScript/out00-PYZ.pyz/flask.app", line 1477, in full_dispatch_request

File "/Users/george/Downloads/flaskApps/flaskScript/build/flaskScript/out00-PYZ.pyz/flask.app", line 1381, in handle_user_exception

File "/Users/george/Downloads/flaskApps/flaskScript/build/flaskScript/out00-PYZ.pyz/flask.app", line 1475, in full_dispatch_request

File "/Users/george/Downloads/flaskApps/flaskScript/build/flaskScript/out00-PYZ.pyz/flask.app", line 1461, in dispatch_request

File "<string>", line 13, in index

File "/Users/george/Downloads/flaskApps/flaskScript/build/flaskScript/out00-PYZ.pyz/flask.templating", line 127, in render_template

File "/Users/george/Downloads/flaskApps/flaskScript/build/flaskScript/out00-PYZ.pyz/jinja2.environment", line 851, in get_or_select_template

File "/Users/george/Downloads/flaskApps/flaskScript/build/flaskScript/out00-PYZ.pyz/jinja2.environment", line 812, in get_template

File "/Users/george/Downloads/flaskApps/flaskScript/build/flaskScript/out00-PYZ.pyz/jinja2.environment", line 774, in _load_template

File "/Users/george/Downloads/flaskApps/flaskScript/build/flaskScript/out00-PYZ.pyz/flask.templating", line 64, in get_source

TemplateNotFound: index.html

While it works fine in the dev setup while executing python flaskScript.py

Upvotes: 16

Views: 23726

Answers (6)

user15824070
user15824070

Reputation:

pyinstaller spec_an_API.py --onefile --add-data "templates";"templates" --add-data "static";"static"

For windows using --onefile

Upvotes: 2

ssarabando
ssarabando

Reputation: 3517

This question is a bit old now, but I was having the same problem (Python 3.4.3, Flask 0.10.1 and PyInstaller 3.1.1) when packaging it to a single file.

I've managed to solve it by adding the following to the initialization script (app\__init__.py):

import sys
import os
from flask import Flask
# Other imports

if getattr(sys, 'frozen', False):
    template_folder = os.path.join(sys._MEIPASS, 'templates')
    app = Flask(__name__, template_folder=template_folder)
else:
    app = Flask(__name__)

# etc

The problem is that when the site is running in packaged form, the templates are inside a directory called _MEIxxxxxx under the temp directory (see this in the PyInstaller Manual) so we have to tell that to Flask.

That is done with the template_folder argument (which I found out about in this answer here and later in the API docs).

Finally, the if is there to ensure that we can still use it unpackaged while developing it. If it is frozen, then the script is packaged and we have to tell Flask where to find the templates; otherwise we're running it in a Python environment (taken from here) and the defaults will work (assuming you're using the standard templates directory of course).

Upvotes: 18

None
None

Reputation: 602

if you're building from a .spec file this can easily be accomplished without altering

__int__.py

1) add your templates and static folder to datas in .spec

2) make sure to add jinja2 to hiddenimports

block_cipher = None

# add templates and static folders to a list
added_files =[
    ('templates/*.html', 'templates'),
    ('static/*.css', 'static'),
    ]

a = Analysis(['run.py'],
             pathex=['/your/app/location'],
             binaries=[],
             datas = added_files, # set datas = added_files list
             hiddenimports=['jinja2.ext'], # be sure to add jinja2 
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher,
             noarchive=False)
             ...

this method is directly from the pyinstaller docs here

Upvotes: 1

toto_tico
toto_tico

Reputation: 19027

If you are trying to create a --onefile executable you will also need to add the directories in the spec file.

  1. In the Python code, find where the application is running and store the path in base_dir:

    import os, sys
    base_dir = '.'
    if hasattr(sys, '_MEIPASS'):
        base_dir = os.path.join(sys._MEIPASS)
    
  2. Pass the proper paths to the Flask app using the `static_folder and template_folder parameters:

    app = Flask(__name__,
            static_folder=os.path.join(base_dir, 'static'),
            template_folder=os.path.join(base_dir, 'templates'))
    
  3. In the spec file, we do need to tell pyinstaller to include the templates and static folder including the corresponding folders in the Analysis section of the pyinstaller:

    a = Analysis(
         ...
         datas=[
           ('PATH_TO_THE_APP\\static\\', 'static'),
           ('PATH_TO_THE_APP\\templates\\', 'templates'),
         ],
         ...
    

A bit of explanation:

The general problem with not finding files after packaging with pyinstaller is that the files will change its path. With the --onefile option your files will be compressed inside the exe. When you execute the exe, they are uncompressed and put in a temporal folder somewhere.

Tha somewhere changes everytime you execute the file, but the running application (not the spec file, but your main python file, say main.py) can be found here:

import os
os.path.join(sys._MEIPASS)

So, the templates files won't be in ./templates, but in os.path.join(os.path.join(sys._MEIPASS), templates). The problem now is that your code won't run unless it is packaged. Therefore, python main.py won't run.

Therefore a condition is required to find the files in the right place:

import os, sys
base_dir = '.'
if hasattr(sys, '_MEIPASS'): # or, untested: if getattr(sys, 'frozen', False):
    base_dir = os.path.join(sys._MEIPASS)

Here is more regarding runtime information in pyinstaller

Upvotes: 15

Ezequiel Casta&#241;o
Ezequiel Casta&#241;o

Reputation: 493

I've just wrote a blog post about this problem and others similar, Create one executable file for a Flask app with PyInstaller

Basically an elegant solution is to execute the following:

Windows

pyinstaller -w -F --add-data "templates;templates" --add-data "static;static" app.py

Linux (NOT TESTED)

pyinstaller -w -F --add-data "templates:templates" --add-data "static:static" app.py

Upvotes: 6

Sathish Kumar
Sathish Kumar

Reputation: 351

if getattr(sys, 'frozen', False):                                                                                                                                     
      template_folder = os.path.join(sys.executable, '..','templates')                                                                                                  
      static_folder = os.path.join(sys.executable, '..','static')                                                                                                       
      app = Flask(__name__, template_folder = template_folder,\                                                                                                         
                              static_folder = static_folder)

copy this code and paste in your file. now pyinstaller looks for static and template folder in your dist directory. Now copy and paste static and template folder in your dist folder. it will works

Upvotes: 4

Related Questions