Using PyInstaller (or similar) to create an executable

Thanks Simon! I have also raised my issue in your Github. Pls give it a glance when you have time.

@hmasdev solution worked for me, with a few notes:

  • streamlit.cli is now streamlit.web.cli

  • The flag_options change to the main_run_cl_Explicit function was necessary

  • Worked fine with Pyinstaller 5.4.1

  • Dragging the exe out of dist was preferable to copying everything into dist

Hi all!
This solution also works for me, however I don’t go through ‘main_run_cl_Explicit’.
In my run_main.py I just used these lines of codes.

import streamlit.web.cli as stcli
import sys

def streamlit_run():
     sys.argv=["streamlit", "run", "main.py", "--global.developmentMode=false"]
     sys.exit(stcli.main())

if __name__ == "__main__":
     streamlit_run()

I have another problem, how to integrate St-Aggrid with Pysintaller???

1 Like

Is it going to work if my main streamlit file is named “main_streamlit.py” instead of “main.py”?
with the change " streamlit.cli._main_run_clExplicit(‘main_streamlit.py’, ‘streamlit run’)
" inside run_main.py file.

Hi @orudne , @kevinlinxc

I have tried these 5 steps…However while running the 5th step, I’m getting the follow error:
399 INFO: PyInstaller: 5.7.0
400 INFO: Python: 3.8.5 (conda)
401 INFO: Platform: Windows-10-10.0.19041-SP0
option(s) not allowed:
–onedir/–onefile
–additional-hooks-dir
makespec options not valid when a .spec file is given

When I use --onedir option in the same command, then it runs fine but the 404 error still occurs when I click on the exe file…
Can anyone help please?

Dear all.

I’m new on this forum, and sorry If I make some mistakes when asking something.
I’ve tried this method to convert simple main.py (as in your example) to exe, procedure was without errors, but when I double clicked on run_main.exe, cmd window appeared for a short, and then disapear and nothing happend after it. What could be a problem?

Also, I don’t know exactly what I must do in this part (picture bellow):

what means add folowing lines to cli.py, is that mean to open cli.py and add in it this line:

${YOUR_CONDA_ENV}/lib/site-packages/streamlit/cli.py

And where to add it before or after :

def _main_run_clExplicit(file, command_line, args=):
streamlit._is_running_with_streamlit = True
bootstrap.run(file, command_line, args)

thx in advance,

best regards,

Adnan

You have to copy the function code inside the cli.py file. Default cli.py doesn’t contain the code for function “_main_run_clExplicit” so it needs to be added manually.

I create a repo to explain the step by step to solve this. And in few hours I’m publishing a video! Give your like and GH star!

I just create a repo with Really minimal Streamlit use case following your step-by-step

Thinking on you I create a repo with Really minimal Streamlit use case! Give a star! The video came out of the oven in a few hours. Se você quiser pode clonar o repo e seguir o passo a passo do README antes mesmo de eu publicar o video que será live no twitchJVCSS and published on youtubeJVCSS

After following steps posted by @hmasdev and @kevinlinxc, I managed to get it working. Thought it would be worthwhile to post some changes I had to make to get this working, especially with newer versions floating around.

I’m working with Streamlit 1.17.0 (boy have things changed a lot), as well as Pyinstaller 5.7.0. I also use Pipenv, but that shouldn’t really matter here.

  1. Still need to create run_main.py, but we no longer need to create _main_run_clExplicit, as we can call bootstrap.run() directly with the proper flags now.
import streamlit.web.bootstrap

if __name__ == "__main__":
    streamlit.web.bootstrap.run(
        "./main.py",
        "streamlit run",
        [],
        {"_is_running_with_streamlit": True},
    )
  1. Create ./hooks/hook-streamlit.py as shown
  2. Create ./.streamlit/config.toml as shown
  3. To create the spec file, I used pyi-makespec --onefile --additional-hooks-dir=./hooks run_main.py instead of pyinstaller --onefile ..., just because it’s quite a bit faster and we don’t care about the binary compiled by pyinstaller at this point anyways. After that, update run_main.spec as shown.
  4. The final executable can then be compiled using pyinstaller run_main.spec --clean (this avoids the invalid makespec options other people have been seeing)
  5. I agree with @kevinlinxc here, it is much simpler to lift the resultant binary out from ./dist into the working directory to run

Hopefully this helps/updates this a bit!

Edit:

After some tweaking, I managed to get this to compile everything into a true single binary, where you don’t need to keep the executable in the repository for it to run. To do this, I place my streamlit app code (i.e. main.py with any st.write() stuff) into a subdirectory called ./application.

  1. We still need to create run_main.py, but we add in the config we previously set in ./.streamlit/config.toml directly and load it in
import os
import streamlit.web.bootstrap

if __name__ == "__main__":
    os.chdir(os.path.dirname(__file__))

    flag_options = {
        "server.port": 8501,
        "global.developmentMode": False,
    }

    streamlit.web.bootstrap.load_config_options(flag_options=flag_options)
    flag_options["_is_running_with_streamlit"] = True
    streamlit.web.bootstrap.run(
        "./application/main.py",
        "streamlit run",
        [],
        flag_options,
    )

  1. We still must create ./hooks/hook-streamlit.py as previously mentioned
  2. We do not need to create ./.streamlit/config.toml anymore. It shouldn’t hurt if it’s there, but the config will not be used
  3. Create specfile using pyi-makespec --onefile --additional-hooks-dir=./hooks run_main.py (or pyinstaller, I just tend to prefer pyi-makespec) and update it with the following:
datas=[
    (
        "<path to lib>/Lib/site-packages/altair/vegalite/v4/schema/vega-lite-schema.json",
        "./altair/vegalite/v4/schema/"
    ),
    (
        "<path to lib>/Lib/site-packages/streamlit/static",
        "./streamlit/static"
    ),
    (   
        "<path to app code>/application",
        "./application"
    )
],
hiddenimports=[
    "streamlit"
],

The last entry to datas is important, as it tells pyinstaller to bundle in all of the streamlit app code.

  1. Compile the final executable with pyinstaller run_main.spec --clean
  2. The final executable can be run anywhere, no need to move it to any specific location!
3 Likes

Hey @charlesmyu , tried the solution which you have suggested. Followed every but of it but when I run the final exectuable I get an error something like this
ModuleNotFoundError: No module named ‘streamlit.runtime.scriptrunner.magic_funcs’.

File "streamlit\runtime\scriptrunner\script_runner.py", line 565, in _run_scriptFile "C:\Users\NikhilRanade\AppData\Local\Temp\_MEI275882\application\main.py", line 1, in <module>
    import streamlit as st

Attaching screenshot for the reference. Any help would be appreciated on this

I was in the same boat but think I have it worked out. In your “run_main.py” file - include an import streamlit.runtime.scriptrunner.magic_funcs line. Not sure why plain import streamlit doesn’t work but this worked for me.

2 Likes

Thanks @littletuna it worked for me also. But I have several other imports in my script for which I am facing below error when I run the .exe file.
No such component directory: ‘C:\Users\NIKHIL~2\AppData\Local\Temp_MEI211242\streamlit_option_menu\frontend\dist’

Do we need to specify these imports somewhere? Thanks for the help in advance.

@Nikhil_Ranade Are you sure you have the hooks created correctly, and the specfile defined with the correct datas? Those would be my first guess, usually any import errors I run into stem from issues with one of those two things

1 Like

Below is my hook-streamlit.py file and the spec file respectively. Is there something I might be missing here? Thanks for the help in advance. :slightly_smiling_face:

from PyInstaller.utils.hooks import copy_metadata
datas = copy_metadata('streamlit')
# -*- mode: python ; coding: utf-8 -*-


block_cipher = None


a = Analysis(
    ['run_main.py'],
    pathex=["C:/Users/NikhilRanade/AppData/Local/Programs/Python/Python38/Lib/site-packages"],
    binaries=[],
    datas=[    (
        "C:/Users/NikhilRanade/AppData/Local/Programs/Python/Python38/Lib/site-packages/altair/vegalite/v4/schema/vega-lite-schema.json",
        "./altair/vegalite/v4/schema/"
    ),
    (
        "C:/Users/NikhilRanade/AppData/Local/Programs/Python/Python38/Lib/site-packages/streamlit/static",
        "./streamlit/static"
    ),
    (   
        "C:/Users/NikhilRanade/Desktop/Nikhil/EXE_ShareMon/application",
        "./application"
    )],
    hiddenimports=["streamlit"],
    hookspath=['./hooks'],
    hooksconfig={},
    runtime_hooks=[],
    excludes=[],
    win_no_prefer_redirects=False,
    win_private_assemblies=False,
    cipher=block_cipher,
    noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)

exe = EXE(
    pyz,
    a.scripts,
    a.binaries,
    a.zipfiles,
    a.datas,
    [],
    name='run_main',
    debug=False,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    upx_exclude=[],
    runtime_tmpdir=None,
    console=True,
    disable_windowed_traceback=False,
    argv_emulation=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file=None,
)

@Nikhil_Ranade Ah, I see - I didn’t realize streamlit-options-menu was an additional package. Try adding it to hiddenimports (i.e. hiddenimports=["streamlit", "streamlit-options-menu"] and it should work!

Sometimes pyinstaller needs some help to recognize the imports. Whenever I get an import error, the first thing I try is adding it to hiddenimports, which forces pyinstaller to look for the package and bundle it in.

1 Like

Dear @charlesmyu ,
Thank for your previous answers. I am trying to find a solution for turning my app into an executable but as I am in a corporate context, IT don’t give us user permission. I built my project using a virtual environment and followed all the steps but I still have an issue. I have a logo used by my application and when I run I have an error because it can’t seem to find it. Is there something I should add in datas with makespecs please ?

@charlesmyu , tried that as well but it did not work :frowning:

# -*- mode: python ; coding: utf-8 -*-


block_cipher = None


a = Analysis(
    ['run_main.py'],
    pathex=["C:/Users/NikhilRanade/AppData/Local/Programs/Python/Python38/Lib/site-packages"],
    binaries=[],
    datas=[    (
        "C:/Users/NikhilRanade/AppData/Local/Programs/Python/Python38/Lib/site-packages/altair/vegalite/v4/schema/vega-lite-schema.json",
        "./altair/vegalite/v4/schema/"
    ),
    (
        "C:/Users/NikhilRanade/AppData/Local/Programs/Python/Python38/Lib/site-packages/streamlit/static",
        "./streamlit/static"
    ),
    (   
        "C:/Users/NikhilRanade/Desktop/Nikhil/EXE_ShareMon/application",
        "./application"
    )],
    hiddenimports=["streamlit","snowflake","snowflake-connector-python","streamlit_option_menu"],
    hookspath=['./hooks'],
    hooksconfig={},
    runtime_hooks=[],
    excludes=[],
    win_no_prefer_redirects=False,
    win_private_assemblies=False,
    cipher=block_cipher,
    noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)

exe = EXE(
    pyz,
    a.scripts,
    a.binaries,
    a.zipfiles,
    a.datas,
    [],
    name='run_main',
    debug=False,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    upx_exclude=[],
    runtime_tmpdir=None,
    console=True,
    disable_windowed_traceback=False,
    argv_emulation=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file=None,
)

Also tried something like this but no luck here as well :frowning:

import sys
sys.path.insert(1, "C:/Users/NikhilRanade/AppData/Local/Programs/Python/Python38/Lib/site-packages/streamlit_option_menu")
from streamlit_option_menu import option_menu

Hello everybody, i have a problem with “on_hover_tabs”
in my main.py i have

from st_on_hover_tabs import on_hover_tabs
import streamlit as st

in my run_main.py ihave

import streamlit.web.cli as stcli
import sys
import streamlit.runtime.scriptrunner.magic_funcs

def streamlit_run():
     sys.argv=["streamlit", "run", "main.py", "--global.developmentMode=false"]
     sys.exit(stcli.main())

if __name__ == "__main__":
     streamlit_run()

in my run_main.spec

# -*- mode: python ; coding: utf-8 -*-


block_cipher = None


a = Analysis(
    ['run_main.py'],
    pathex=[],
    binaries=[],
    datas=[
        (
            "C:/SC/Python39/Lib/site-packages/altair/vegalite/v4/schema/vega-lite-schema.json",
            "./altair/vegalite/v4/schema/"
        ),
        (
            "C:/SC/Python39/Lib/site-packages/streamlit/static",
            "./streamlit/static"
        ),
        (
            "C:/SC/Python39/Lib/site-packages/st_on_hover_tabs/frontend/build/static",
            "./st_on_hover_tabs/static"
        ),
        (
            "C:/SC/Pycharm_Project/Tools_database",
            ".application"
        ),
    ],
    hiddenimports=[
        "streamlit"
    ],
    hookspath=['./hooks'],
    hooksconfig={},
    runtime_hooks=[],
    excludes=[],
    win_no_prefer_redirects=False,
    win_private_assemblies=False,
    cipher=block_cipher,
    noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)

exe = EXE(
    pyz,
    a.scripts,
    a.binaries,
    a.zipfiles,
    a.datas,
    [],
    name='run_main',
    debug=False,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    upx_exclude=[],
    runtime_tmpdir=None,
    console=True,
    disable_windowed_traceback=False,
    argv_emulation=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file=None,
    icon=['style\\pstudio.ico'],
)

I have an error when i launch my exe file

ImportError: cannot import name 'on_hover_tabs' from 'st_on_hover_tabs' (unknown location)

Traceback:


File "streamlit\runtime\scriptrunner\script_runner.py", line 565, in _run_scriptFile "C:\SC\Pycharm_Project\Tools_database\dist\main.py", line 3, in <module>
    from st_on_hover_tabs import on_hover_tabs

Thanks in advance for your help.