Source code for runtimepy.net.server.websocket.state

"""
A module implementing a stateful data interface for per-connection tabs.
"""

# built-in
from collections import defaultdict
from dataclasses import dataclass
import logging
from typing import Optional

# third-party
from vcorelib.io import ByteFifo
from vcorelib.logging import ListLogger

# internal
from runtimepy.channel.environment.base import ValueMap
from runtimepy.message import JsonMessage
from runtimepy.net.server.websocket.data import (
    RuntimepyDataWebsocketConnection,
)
from runtimepy.primitives import AnyPrimitive

# (value, nanosecond timestamp)
Point = tuple[str | int | float | bool, int]


[docs] @dataclass class TabState: """Stateful information relevant to individual tabs.""" shown: bool tab_logger: ListLogger points: dict[str, list[Point]] primitives: dict[str, AnyPrimitive] callbacks: dict[str, int] latest_ui_values: ValueMap _loggers: list[logging.Logger] binary: ByteFifo
[docs] def frame( self, time: float, data_connection: Optional[RuntimepyDataWebsocketConnection] = None, ) -> JsonMessage: """Handle a new UI frame.""" # Not used yet. del time result: JsonMessage = {} # Handle log messages. if self.tab_logger: result["log_messages"] = self.tab_logger.drain_str() # Handle channel updates. if self.points: result["points"] = self.points self.points = defaultdict(list) # Forward binary data (not used yet). msg = self.binary.pop(self.binary.size) if msg and data_connection is not None: data_connection.send_message(msg) # pragma: nocover return result
[docs] def clear_telemetry(self) -> None: """Clear all telemetry interactions.""" # Remove callbacks for primitives. for name, val in self.callbacks.items(): self.primitives[name].remove_callback(val) self.callbacks.clear() self.primitives.clear() # Clear points. self.points.clear() self.binary.pop(self.binary.size)
[docs] def clear_loggers(self) -> None: """Clear all logging handlers.""" # Remove handlers. for logger in self._loggers: logger.removeHandler(self.tab_logger) self._loggers.clear() self.tab_logger.drain_str() self.clear_telemetry()
[docs] def add_logger(self, logger: logging.Logger) -> None: """Add a logger.""" if logger not in self._loggers: logger.addHandler(self.tab_logger) self._loggers.append(logger)
[docs] @staticmethod def create() -> "TabState": """Create a new instance.""" return TabState( False, ListLogger.create(), defaultdict(list), {}, {}, {}, [], ByteFifo(), )