Stopping an asyncio producer-consumer script causes error: session_state has no key

Summary

When I “stop” an asyncio producer-consumer script, I get the error: session_state has no key “xyz”, where xyz is a state that I have already initialized at the beginning of the run.

Steps to reproduce

Run the code below, then click on the “Stop” button, which is to the right of “RUNNING…”

Code snippet:

Code is taken from the discussion in the link, with some modifications to get it working.

# based on https://discuss.streamlit.io/t/best-fastest-practice-to-display-live-2d-data/19895/4
import asyncio
from datetime import datetime

import cv2
import numpy as np
import streamlit as st

QUEUE_SIZE = 1000
SIZE_IMAGE = 512


async def produce_images(queue, delay):
    while True:
        # Add bars depending on state count
        n = st.session_state.produced_images % SIZE_IMAGE
        m = st.session_state.produced_images % SIZE_IMAGE
        st.session_state.produced_images += 1

        image = np.random.random((SIZE_IMAGE, SIZE_IMAGE)).astype(np.float32)
        image[n: n + 10] = 0
        image[:, m: m + 10] = 1

        _ = await queue.put(cv2.cvtColor(image, cv2.COLOR_GRAY2BGR))
        _ = await asyncio.sleep(delay)


async def consume_images(image_placeholder, queue_size_placeholder, queue, delay):
    while True:
        image = await queue.get()
        image_placeholder.image(
            image,
            caption=f"Produced images: {st.session_state.produced_images}, "
                    f"Consumed images: {st.session_state.consumed_images}, {str(datetime.now())}\n"
        )
        queue_size_placeholder.metric(
            f"In queue (queue size is {QUEUE_SIZE})", queue.qsize()
        )
        st.session_state.consumed_images += 1
        queue.task_done()
        _ = await asyncio.sleep(delay)


async def run_app(image_placeholder, queue_size_placeholder, produce_delay, consume_delay):
    queue = asyncio.Queue(QUEUE_SIZE)
    _ = await asyncio.gather(
        produce_images(queue, produce_delay),
        consume_images(image_placeholder, queue_size_placeholder, queue, consume_delay),
    )


# ACTUAL APP #
if __name__ == "__main__":
    st.set_page_config(
        layout="wide",
        initial_sidebar_state="auto",
        page_title="Asyncio test",
        page_icon=None,
    )

    if "produced_images" not in st.session_state:
        st.session_state.produced_images = 0
    if "consumed_images" not in st.session_state:
        st.session_state.consumed_images = 0

    st.title("Hello random image!")
    produce_delay = 1 / st.sidebar.slider(
        "Produce images Frequency (img / second)", 1, 100, 10
    )
    consume_delay = 1 / st.sidebar.slider(
        "Display images Frequency (img / second)", 1, 100, 10
    )
    c1, c2 = st.columns(2)
    image_placeholder = c1.empty()
    queue_size_placeholder = c2.empty()

    asyncio.run(
        run_app(
            image_placeholder,
            queue_size_placeholder,
            produce_delay,
            consume_delay,
        )
    )

Expected behavior:

Code should stop without raising any error.

Actual behavior:

When script is stopped, it (almost always) hits the error

AttributeError: st.session_state has no attribute “produced_images”. Did you forget to initialize it? More info: Add statefulness to apps - Streamlit Docs

However, once in a rare while there would be no error.

One can always “continue” the code by clicking on the “Rerun” menu item.

Debug info

  • Streamlit version: 1.21.0
  • Python version: 3.9.6
  • Using pip venv
  • OS version: Windows 10
  • Browser version: Edge

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