Streamlit to Executable

Create a Virtual Environment

pyenv virtualenv <version> .<env-name>
# or
python -m venv .<env-name>
# THE DOT IS IMPORTANT!

Activate the Virtual Environment

pyenv activate <env-name>
# or
.<env-name>\\Scripts\\activate.bat

Verify the Virtual Environment

python --version

Deactivate the Virtual Environment

pyenv deactivate
# or
.<env-name>\\Scripts\\deactivate.bat

Install Streamlit and Other Required Libraries

# You can use the latest version
pip install streamlit pyinstaller

Add the Main File (app.py)

echo > app.py

Create an Entry Point for the Executable (run_app.py)

echo > run_app.py

Add Content to Your Files

  • app.py:
import streamlit as st

if __name__ == '__main__':
    st.header("Hello, World!")
  • run_app.py
from streamlit.web import cli

# This import path depends on your Streamlit version
if __name__ == '__main__':
    cli._main_run_clExplicit('app.py', args=['run'])
    # We will CREATE this function inside our Streamlit framework

Navigate to the Streamlit Path

In the version we are using, it is located at: .env\Lib\site-packages\streamlit\web\cli.py

Add the Magic Function

# ... def main(log_level="info"):
# [...]
# You can use any name you prefer as long as it starts with an underscore
def _main_run_clExplicit(file, is_hello, args=[], flag_options={}):
    bootstrap.run(file, is_hello, args, flag_options)

# ...if __name__ == "__main__":
# ...    main()

Create a Hook to Get Streamlit Metadata

  • .\hooks\hook-streamlit.py
from PyInstaller.utils.hooks import copy_metadata

datas = copy_metadata('streamlit')

Compile the App

Run the following command to create the first run_app.spec file. Note that if you are using auto-py-to-exe, you can't edit spec files here; you should edit them from the interface in the advanced options.

pyinstaller --onefile --additional-hooks-dir=./hooks run_app.py --clean
# --onefile: Create a single output file
# --clean: Delete cache and remove temporary files before building
# --additional-hooks-dir: An additional path to search for hooks. This option can be used multiple times.

Create Streamlit Configuration Files

You can add these files to your project's root and the output folder, or just the output folder.

  • .streamlit\config.toml
[global]
developmentMode = false

[server]
port = 8502

Copy the Configuration Files to the Output Folder

xcopy /s /e .streamlit output/.streamlit
# Select D = directory

Copy app.py to the Output Folder

copy app.py output/app.py

Add the Data to the New Hook in run_app.spec

...
a = Analysis(
    ...
    datas=[
        (".env/Lib/site-packages/altair/vegalite/v5/schema/vega-lite-schema.json",
        "./altair/vegalite/v5/schema/"),
        (".env/Lib/site-packages/streamlit/static",
        "./streamlit/static"),
        (".env/Lib/site-packages/streamlit/runtime",
        "./streamlit/runtime"),
    ]
    ...
)
...

Notes

# 
# this path pair should be in that way
# but I believe it is because we add the tuple as this templete
# (absolut_path, parent_path)
# so for files that is in the root of `Lib/site-packages` 
# We can add only the dot as parent 
# i.e: (".envir/Lib/site-packages/wmi.py",".")
# for folders the behaviour is the same

Build the Executable

pyinstaller run_app.spec --clean

🎈 It's done! run your run_app.exe file and see the magic 🪄

Huge Thanks To: hmasdev
I'm organizing the solution from  hmasdev in the Streamlit Forum