Bind view to Singleton attribute

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.

Fixed

Misregard of streamlit running on the MainThread by default. Works fine if your run the incr_multiple method in a separate thread (and change method architecture to not lock the Singleton).

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.