Context
Hi,
I am trying to develop a graph viz with a Python backend. The graph in itself is rendered through html+js (using vis.js library). I simplified the issue I am having for the sake of clarity.
The architectural choice for my backend is to use singletons for some viz and result store components. I noticed something that I didn’t expect: Streamlit freezes (grey out, no view update) my components.html and it is actually true for any Python object impacted (i.e. imported/set) by the running script.
I am running this locally with Python 3.10.4, streamline 1.39.0
Minimal reproductible
File structure
$ tree .
.
├── app.py
├── helper.py
└── metaclasses.py
Python
In app.py
import time
from metaclasses import ThreadSafeSingleton
class TestSingleton(metaclass=ThreadSafeSingleton):
def __init__(self):
self.count = 0
@property
def count_html(self):
return f"<p> Count is {self.count} </p>"
def incr(self):
self.count += 1
def incr_multiple(self):
for _ in range(5):
self.count += 1
time.sleep(1)
In metaclasses.py
import threading
from typing import Any, Dict
class ThreadSafeSingleton(type):
"""
Thread safe implementation of a singleton with a destroy method for reset
"""
_instances = {}
_singleton_locks: Dict[Any, threading.Lock] = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
if cls not in cls._singleton_locks:
cls._singleton_locks[cls] = threading.Lock()
with cls._singleton_locks[cls]:
if cls not in cls._instances:
cls._instances[cls] = super(
ThreadSafeSingleton, cls
).__call__(*args, **kwargs)
return cls._instances[cls]
@classmethod
def destroy(mcs, cls):
if cls not in cls._singleton_locks:
cls._singleton_locks[cls] = threading.Lock()
with cls._singleton_locks[cls]:
if cls in mcs._instances:
del mcs._instances[cls]
In app.py
# --- Testing ---
import streamlit as st
import streamlit.components.v1 as components
from testing import TestSingleton
# gets updated fine for incr as exec done when view should update
# however, doesn't update incrementally when incr_multiple
if st.button("Increment by one"):
TestSingleton().incr()
if st.button("Increment mutiple"):
TestSingleton().incr_multiple()
st.markdown("Count attribute: ")
st.write(TestSingleton().count)
st.markdown("Count HTML attribute: ")
components.html(TestSingleton().count_html)
Question
What can I do so that the streamlit views of the count and count_html properties incrementally update as the incr_multiple() updates them ?
Thank you very much for any time spent into this. It is very possible that I missed something in the documentation that could help so please let me know if you have ideas.