How can I include assets of a dash app into an exe file created with pyinstaller --onefile?

I have the same question posted on stackoverflow, but with no answer: python - How can I include assets of a dash app into an exe file created with pyinstaller --onefile? - Stack Overflow

Basically, it seems like dash is unable to access the asset folder in a temp directory. I have the following code to get the temp directory:

# get temp working directory
import sys
import os
def resource_path(relative_path):

# get absolute path to resource
    try:
        # PyInstaller creates a temp folder and stores path in _MEIPASS
        base_path = sys._MEIPASS
    except Exception:
        base_path = os.path.abspath(".")

    return os.path.join(base_path, relative_path)

print(resource_path("assets"))

This gives me a the path to the current working directory/assets. It works in an anaconda environment as well when bundling the app into an .exe file, showing me the path where the assets are in the console.

But the next part is where I run into trouble:

# create app
app = dash.Dash(__name__, assets_url_path=resource_path("assets"))

For some reason, this only works in the Anaconda virtual environment, but not when running the code from .exe. The dash app opens just fine, but the css is not loaded from .exe, despite print(resource_path("assets")) showing the temporary path to which I can navigate and see the css file…

What am I missing?

@DataWiz, have you managed to solve this problem? In addition to an assets folder, I have a data folder as well. I still could not manage to successfully create an .exe file. Any input is highly apperciated.

Sorry, I have since abandoned the idea of pyinstaller and dash and moved to a web app. I’d be curious to know if it has become any easier though.

@abeljads did you find a solution? I can package my app without issue, but my css file doesn’t get recognized so clearly packaging the assets folder isn’t working.

I have not got any solution yet. Can you share your code that is working for you without including the asset folder? Thanks in advance.

@abeljads sure! Note that I’m using a windows machine. For Linux I think you just need to replace semi-colons with colons.

I actually did ultimately get it to work! I think the second --add-data option might not be necessary, but its working for me now and I’m not going to change it :slight_smile:

pyinstaller --onefile --hidden-import pyodbc --add-data="assets;assets" --add-data "assets\base.css;assets" --additional-hooks-dir=hooks app.py

You might have to make some changes, like the hidden import command line option.

Importantly, I have a folder in the same directory as the app.py file called ā€œhooksā€. In it is a handful of individual .py files. They all follow the same structure, though, and just change the package they’re supporting. I’ll give an example with dash bootstrap components. The name of the file is hook-dash_boostrap_components.pyand the code in the file is below.

# hooks/hook-dash_boostrap_components.py

from PyInstaller.utils.hooks import collect_all

datas, binaries, hiddenimports = collect_all('dash_bootstrap_components')

For my application I had to create these files for dash bootstrap components, dash core components, dash html components, dash renderer, dash table, and flask compress. I determined these were needed via trial and error.

1 Like

It’s been several years since this thread was last opened but I was unable to find a single resource that showed how to include javascript/css/etc. (basically anything in the assets folder) in the dash executable and have it actually work.

I was finally able to get it working and it’s a relatively simple workaround.

The first thing I did was follow this comment/thread on allowing dash to see the assets folder:

But instead of following this exactly (since it wasn’t working), I modified the call in dash.Dash() to look like this app = dash.Dash(__name__, assets_folder=find_data_file('_internal/assets/'), include_assets_files=True). The find_data_file function is included at the post above. The reason for the ā€˜_internal’ inclusion was that I noticed once the app is packaged up as an executable when running pyinstaller app.spec all of the included modules (including the assets folder) were ending up in there instead of at the same level as the executable. Since, presumably, assets needs to be accessible at the same level as the executable, I figured I should just point the app to that location beforehand. This also necessitates your assets folder being placed into a directory called ā€˜_internal’ before you package everything up. You can test out the app to make sure it still has access to those files when running via Python on command line.

The app.spec file also needs to include the ā€˜_internal’ in the path to the assets folder. So your ā€˜datas’ variable should look like this:

datas=[('_internal/assets/javascript_stuff.js','assets'), 
             ...,
             ...],

Once I did that, the javascript was accessible by the executable and worked just as expected.

I wanted to share on the off chance someone else needed a workaround for this still. If anyone has any better solutions, I’m still open to them. Nothing seems to have been negatively affected otherwise.