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.
- Still need to create
run_main.py
, but we no longer need to create_main_run_clExplicit
, as we can callbootstrap.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},
)
- Create
./hooks/hook-streamlit.py
as shown - Create
./.streamlit/config.toml
as shown - To create the spec file, I used
pyi-makespec --onefile --additional-hooks-dir=./hooks run_main.py
instead ofpyinstaller --onefile ...
, just because it’s quite a bit faster and we don’t care about the binary compiled bypyinstaller
at this point anyways. After that, updaterun_main.spec
as shown. - The final executable can then be compiled using
pyinstaller run_main.spec --clean
(this avoids the invalid makespec options other people have been seeing) - 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
.
- 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,
)
- We still must create
./hooks/hook-streamlit.py
as previously mentioned - 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 - Create specfile using
pyi-makespec --onefile --additional-hooks-dir=./hooks run_main.py
(orpyinstaller
, I just tend to preferpyi-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.
- Compile the final executable with
pyinstaller run_main.spec --clean
- The final executable can be run anywhere, no need to move it to any specific location!