I am rewriting a streamlit app that is an interface to a laboratory management system (LMS). This means, that I have to make a lot of requests to that LMS through its python library (PyBIS). The way that library works is, that I import a class and instantiate it, and then call a method to login to the server. This login process however takes a couple of seconds. This means, I want to call the "login" method only once, and use that instance, lets call it pybis_instance
, for all of my following calls.
Now to my issue: I have about 10 different functions which I use to get data from the LMS. They all need that instance I mentioned before, to get their data. Almost all of my other code uses one of those 10 functions at some point. Now, I have two options that I know of to pass that instance to those functions:
- Pass that instance as an argument to the function. This was my first way of doing this and seems like the "right" way. However, now my code is cluttered with the
pybis_instance
parameter, almost every function needs it, mostly just to pass it on to the next function which is called inside. - Use a streamlits session_state to store that instance. This makes things a lot easier, since now I only need to care about
pybis_instance
in my 10 functions which interact with the LMS directly. However, the session_state function of streamlit is kind of the same thing as a global variable, with the exception that it is namespaced into the streamlit package. And I've always learned "global variables are bad, don't use them".
Now, is there a good way to 1. not clutter my code with the same parameter/argument in every function and 2. not use a global variable instead? I can't come up with a good solution to this. I thought about wrapping my instance and my 10 functions into their own class and to just be able to import that, but for the login I still need to instantiate that class and still need to pass that exact instance around. Maybe there's some way to write a helper function which checks if an instance of that class already exists?
At the end some code examples to illustrate how 1. and 2. work at the moment:
1 - instance as an argument
from pybis import Openbis
def login():
pybis_instance = Openbis(URL)
pybis_instance.login(USER, PASSWORD)
return pybis_instance
## in app.py:
pybis_instance = login()
create_some_interface(pybis_instance, ...)
## in interface.py
def create_some_interface(pybis_instance: Openbis, ...):
...
some_other_routine(pybis_instance, ...)
...
...
## in get.py
def get_some_data(pybis_instance):
return pybis_instance.load_data()
2 - instance in global variable
## in app.py
st.session_state["pybis_instance"] = login()
## in get.py
def get_some_data():
return st.session_state["pybis_instance"].load_data()
Edit 1
would it work to assign that instance to a variable and then to import that variable into other functions? is there some caveat?
## app.py
instance = login()
## get.py
from app import instance
def get_some_data():
instance.load_data()
However, streamlit re-runs app.py all the time - which would cause the code to execute. If I assign a variable in a module, is it executed only once? Maybe I could put instance = login()
at the top of get.py
? -> sadly doesn't work, I need to provide login details, so I can't instantiate at import
Edit 2
Also, maybe a singleton pattern could work here? Singleton
class
and there will be a natural place where to put thepybis_instance