Source code for webinterface.pages.base_pages.utils.session_state

"""Helpers for inspecting Streamlit ``st.session_state`` on the debug page.

Provides two small UI builders used by ``pages/0_debug_session_state.py``:

- :func:`ui_overview_table` renders a table of every key currently in
  ``st.session_state`` with its type and an abbreviated value preview.
- :func:`ui_key_inspector` renders a selectbox to pick a single key and shows
  its full value.

Both are deliberately defensive: a value that cannot be rendered must not crash
the whole page, so failures are caught and shown inline.
"""

from __future__ import annotations

import pprint
from typing import Any

import pandas as pd
import streamlit as st

_MAX_SCALAR_REPR = 200  # max chars for an inline scalar repr before truncation
_MAX_FULL_REPR = 20000  # max chars for the full-value view in the inspector


def _type_name(value: Any) -> str:
    """Return the short class name of a value.

    Parameters
    ----------
    value : Any
        The value to describe.

    Returns
    -------
    str
        The value's type name (e.g. ``"DataFrame"``, ``"list"``).
    """
    return type(value).__name__


def _abbreviate(value: Any, max_list_repr: int = 5) -> str:
    """Return a short, human-readable preview of a session-state value.

    Lists/tuples/sets are capped at ``max_list_repr`` items and DataFrames and
    Series are summarized by shape/length rather than dumped in full.

    Parameters
    ----------
    value : Any
        The value to summarize.
    max_list_repr : int, optional
        Maximum number of items shown inline for sequences and dict keys,
        by default 5.

    Returns
    -------
    str
        A compact preview string.
    """
    try:
        if isinstance(value, pd.DataFrame):
            cols = list(map(str, value.columns[:max_list_repr]))
            more = f", +{value.shape[1] - max_list_repr} more" if value.shape[1] > max_list_repr else ""
            return f"DataFrame(shape={value.shape}, columns=[{', '.join(cols)}{more}])"
        if isinstance(value, pd.Series):
            return f"Series(len={len(value)}, dtype={value.dtype})"
        if isinstance(value, (list, tuple, set)):
            seq = list(value)
            shown = ", ".join(repr(x) for x in seq[:max_list_repr])
            more = f", +{len(seq) - max_list_repr} more" if len(seq) > max_list_repr else ""
            open_b, close_b = {list: ("[", "]"), tuple: ("(", ")"), set: ("{", "}")}[type(value)]
            return f"{type(value).__name__}{open_b}{shown}{more}{close_b}"
        if isinstance(value, dict):
            keys = list(value.keys())
            shown = ", ".join(repr(k) for k in keys[:max_list_repr])
            more = f", +{len(keys) - max_list_repr} more" if len(keys) > max_list_repr else ""
            return f"dict(len={len(keys)}, keys={{{shown}{more}}})"
        text = repr(value)
        return text if len(text) <= _MAX_SCALAR_REPR else text[:_MAX_SCALAR_REPR] + " …"
    except Exception as exc:  # never let preview rendering break the table
        return f"<unrepresentable: {exc}>"


[docs] def ui_overview_table(max_list_repr: int = 5) -> None: """Render an overview table of all current ``st.session_state`` keys. Each row shows the key, the value's type, and an abbreviated preview. Parameters ---------- max_list_repr : int, optional Maximum number of items shown inline for sequences and dict keys, by default 5. """ state = st.session_state keys = list(state.keys()) st.subheader("Overview") st.caption(f"{len(keys)} key(s) in st.session_state") if not keys: st.info("Session state is empty.") return rows = [] for key in keys: try: value = state[key] rows.append({"key": str(key), "type": _type_name(value), "preview": _abbreviate(value, max_list_repr)}) except Exception as exc: # robust to keys that raise on access rows.append({"key": str(key), "type": "?", "preview": f"<error: {exc}>"}) overview = pd.DataFrame(rows, columns=["key", "type", "preview"]).sort_values("key").reset_index(drop=True) st.dataframe(overview, use_container_width=True, hide_index=True)
[docs] def ui_key_inspector(max_list_repr: int = 5) -> None: """Render a per-key inspector for ``st.session_state``. A selectbox chooses one key; its full value is shown using a widget appropriate to the value's type (DataFrame/Series as tables, collections as pretty-printed text, scalars as code). Parameters ---------- max_list_repr : int, optional Maximum number of items shown in the selectbox help preview, by default 5. """ state = st.session_state keys = list(state.keys()) st.subheader("Inspect a key") if not keys: st.info("Session state is empty.") return # Map the displayed (string) label back to the real key, which may be a UUID # object or other non-string used as a widget key. label_to_key = {str(k): k for k in keys} selected_label = st.selectbox("Select a session-state key", sorted(label_to_key.keys())) if selected_label is None: return actual_key = label_to_key[selected_label] try: value = state[actual_key] except Exception as exc: st.error(f"Could not read key {selected_label!r}: {exc}") return st.markdown(f"**Type:** `{_type_name(value)}` — **preview:** `{_abbreviate(value, max_list_repr)}`") if isinstance(value, pd.DataFrame): st.caption(f"shape={value.shape}") st.dataframe(value, use_container_width=True) elif isinstance(value, pd.Series): st.caption(f"len={len(value)}, dtype={value.dtype}") st.dataframe(value.to_frame(name=str(actual_key)), use_container_width=True) elif isinstance(value, (dict, list, tuple, set)): text = pprint.pformat(value, width=100) st.code(text if len(text) <= _MAX_FULL_REPR else text[:_MAX_FULL_REPR] + "\n… (truncated)", language="python") else: text = repr(value) st.code(text if len(text) <= _MAX_FULL_REPR else text[:_MAX_FULL_REPR] + " …", language="python")