python – Package streamlit app and run executable on windows

this is my first question on Stackoverflow. I hope my question is clear, otherwise let me know and don’t hesitate to ask me more details.

I’m trying to package a streamlit app for a personal project. I’m developing under linux but I have to deploy the app on Windows. I want it to be a standalone executable, which once run opens the browser tab to display the app, and exits when the tab is closed. I would like to use pynsist library to package the app (already used for another project and it worked fine).

I followed the suggestion found in this discussion. It worked fine on ubuntu, and apparently also on Windows after packaging the app with pynsist. “Apparently” because the executable run, but no browser tab was open to display the app.

Here is some snippets of my code.

Project structure

|- installer.cfg
|- src
    |- main.py
    |- run_app.py

main.py

import streamlit as st

st.title("Test")
st.title("My first app deployed with Pynsist!")

run_app.py
(EDIT 2 after comment by Thomas K)

import os
import subprocess
import sys

from src.config import EnvironmentalVariableNames as EnvVar, get_env

def main():
    executable = sys.executable
    result = subprocess.run(
        f"{executable} -m streamlit run {os.path.join(get_env(EnvVar.EMPORIO_VESTIARIO_DASHBOARD_WORKING_DIR), 'src', 'main.py')}",
        shell=True,
        capture_output=True,
        text=True,
    )


if __name__ == "__main__":
    main()

EMPORIO_VESTIARIO_DASHBOARD_WORKING_DIR is an environmental variable to make the app work on both linux and windows (on windows, it is set to the installation directory).

pynsist installer.cfg

EDIT: including dependencies of streamlit discovered through pip list

EDIT 2: added MarkupSafe as dependency of Jinja2

[Application]
name=Emporio Vestiario Dashboard
version=0.1.0
# How to lunch the app - this calls the 'main' function from the 'myapp' package:
entry_point=src.run_app:main
icon=resources/caritas-logo.ico

[Python]
version=3.8.10
bitness=64

[Include]
# Packages from PyPI that your application requires, one per line
# These must have wheels on PyPI:
pypi_wheels = altair==4.1.0
    astor==0.8.1
    attrs==21.2.0
    backcall==0.2.0
    backports.zoneinfo==0.2.1
    base58==2.1.0
    bleach==4.1.0
    blinker==1.4
    cachetools==4.2.2
    certifi==2021.5.30
    cffi==1.14.6
    charset-normalizer==2.0.6
    click==7.1.2
    decorator==5.1.0
    defusedxml==0.7.1
    distlib==0.3.3
    entrypoints==0.3
    idna==3.2
    jsonschema==3.2.0
    mistune==0.8.4
    mypy-extensions==0.4.3
    numpy==1.21.1
    packaging==21.0
    pandas==1.3.3
    pandocfilters==1.5.0
    parso==0.8.2
    pillow==8.3.2
    platformdirs==2.4.0
    prompt-toolkit==3.0.20
    protobuf==3.18.0
    pyarrow==5.0.0
    pycparser==2.20
    pydeck==0.7.0
    pyparsing==2.4.7
    pyrsistent==0.18.0
    python-dateutil==2.8.2
    pytz==2021.1
    requests==2.26.0
    requests-download==0.1.2
    send2trash==1.8.0
    setuptools==57.0.0
    six==1.14.0
    smmap==4.0.0
    streamlit==0.89.0
    terminado==0.12.1
    testpath==0.5.0
    toml==0.10.2
    tomli==1.2.1
    toolz==0.11.1
    tornado==6.1
    traitlets==5.1.0
    typing-extensions==3.10.0.2
    tzlocal==3.0
    urllib3==1.26.7
    validators==0.18.2
    Jinja2==3.0.1
    MarkupSafe==2.0.1

Looking at the executable output on Windows, the current working directory is correctly printed, but no other output (streamlit app initialization message, or error messages) is printed. I tried to open the browser and go to localhost:8501but I got connection error.

Any hints on how to make the code execute and automatically open the browser tab? Any help is greatly appreciated!

EDIT: as pointed out in the comment to the last package in installer.cfg, the app (with Jinja2 dependency) is correctly installed on windows, but when launched, the app still cannot find Jinja2 dependency. This is the traceback:

Traceback (most recent call last):
  File "Emporio_Vestiario_Dashboard.launch.pyw", line 34, in <module>
    from src.run_app import main
  File "C:Userstantardinidevelopcaritaspkgssrcrun_app.py", line 6, in <module>
    import streamlit
  File "C:Userstantardinidevelopcaritaspkgsstreamlit__init__.py", line 75, in <module>
    from streamlit.delta_generator import DeltaGenerator as _DeltaGenerator
  File "C:Userstantardinidevelopcaritaspkgsstreamlitdelta_generator.py", line 70, in <module>
    from streamlit.elements.arrow import ArrowMixin
  File "C:Userstantardinidevelopcaritaspkgsstreamlitelementsarrow.py", line 20, in <module>
    from pandas.io.formats.style import Styler
  File "C:Userstantardinidevelopcaritaspkgspandasioformatsstyle.py", line 49, in <module>
    jinja2 = import_optional_dependency("jinja2", extra="DataFrame.style requires jinja2.")
  File "C:Userstantardinidevelopcaritaspkgspandascompat_optional.py", line 118, in import_optional_dependency
    raise ImportError(msg) from None
ImportError: Missing optional dependency 'Jinja2'. DataFrame.style requires jinja2. Use pip or conda to install Jinja2.

EDIT 2: thanks to the helpful hints by Thomas K, I came up with half a solution. The app runs and streamlit is started.

But.

These are the log messages:

  Welcome to Streamlit!

  If you're one of our development partners or you're interested in getting
  personal technical support or Streamlit updates, please enter your email
  address below. Otherwise, you may leave the field blank.

  Email:
2021-10-11 20:56:53.202 WARNING streamlit.config:
Warning: the config option 'server.enableCORS=false' is not compatible with 'server.enableXsrfProtection=true'.
As a result, 'server.enableCORS' is being overridden to 'true'.

More information:
In order to protect against CSRF attacks, we send a cookie with each request.
To do so, we must specify allowable origins, which places a restriction on
cross-origin resource sharing.

If cross origin resource sharing is required, please disable server.enableXsrfProtection.
           
2021-10-11 20:56:53.202 DEBUG   streamlit.logger: Initialized tornado logs
2021-10-11 20:56:53.202 ERROR   streamlit.credentials: 

It seems that the execution of the app is stopped becuase it is waiting for some credentials. I found here that a .streamlit/credentials.toml can be added, but I’m not sure on the exact location on windows. I’ve also tried to explicitly add --server.headless=false in the subprocess.run command, but again with no effect.

Why the app doesn’t start automatically like on Linux? Is there a way to start the app without additional configurations by the user?

Leave a Comment