State Management - edgeof8/tIRC GitHub Wiki
tIRC employs a centralized StateManager component to robustly manage all client state. This system is designed to be the single source of truth for connection details, session information, user preferences, and runtime data, ensuring consistency and reliability across the entire application.
Refer to the Core Components page for StateManager's role in the broader architecture.
Key Features of tIRC's State Management
As highlighted in the README.md and architectural documents:
- Centralized Authority: The
StateManageris the exclusive source of truth for all runtime state. - Thread Safety: All access and modifications to state are thread-safe, crucial for an asynchronous application.
- Persistence: State is automatically persisted to disk (typically
state.json) and restored on startup. This includes:- Current connection details (
ConnectionInfodataclass). - Authentication state (SASL/NickServ info).
- Connection statistics (attempts, last error).
- List of joined channels and their status.
- User preferences and client settings (logging levels, DCC settings).
- Message history and scrollback positions for each context.
- Current connection details (
- Validation: An extensible validator system (
StateValidator) ensures data integrity for state updates, including complex dataclasses likeConnectionInfo. - Change Notifications: A fine-grained event-based notification system (
StateChange) allows other components and scripts to react to specific state changes.
Core Concepts
StateManager
The primary class responsible for all state operations.
- Initialization: Takes parameters like state file path, auto-save settings, and validation toggles.
- Key Methods:
get(key, default=None): Retrieve a state value.set(key, value, metadata=None): Set a state value, triggering validation and change handlers.delete(key): Remove a state value.get_connection_info() -> Optional[ConnectionInfo]: Get current connection details.set_connection_info(info: ConnectionInfo): Update connection details.get_connection_state() -> ConnectionState: Get current connection status enum.set_connection_state(state: ConnectionState, error=None, config_errors=None): Update connection status.register_validator(key, validator): Add a custom validator for a state key.register_change_handler(key, handler): Subscribe to changes for a specific state key.register_global_handler(handler): Subscribe to all state changes.save_state(): Manually trigger persistence to disk.load_state(): Manually trigger loading from disk (usually handled at startup).
ConnectionInfo Dataclass
A structured dataclass (tirc_core.state_manager.ConnectionInfo) that holds all details related to a specific server connection (server address, port, nick, channels, passwords, SASL details, etc.). This object is the primary way connection-specific state is managed within the StateManager.
ConnectionState Enum
Represents the various states of a connection (e.g., DISCONNECTED, CONNECTING, REGISTERED, READY, ERROR, CONFIG_ERROR). Stored under a key like "connection_state".
StateChange Dataclass
An object dispatched to handlers when state changes, containing:
key: The state key that changed.old_value: The value before the change.new_value: The new value.change_type: Enum (CREATED,UPDATED,DELETED,VALIDATED,INVALIDATED).timestamp: When the change occurred.metadata: Additional context.
How State is Used
- Initialization: On startup,
StateManagerloads persisted state fromstate.json. - Configuration vs. Runtime State:
AppConfigloads static server configurations fromtirc_config.iniintoServerConfigobjects.- When a connection is initiated (e.g., via
/connector auto-connect), the relevantServerConfigdata is used to create a dynamicConnectionInfoinstance, which is then set in theStateManager. ThisConnectionInfois the live, mutable representation of the current connection's state.
- Connection Lifecycle:
ConnectionOrchestratorandIRCClient_LogicupdateConnectionInfoandConnectionStateviaStateManagermethods throughout the connection process.- Validators ensure connection details are valid before attempting to connect.
- Other components (like UI, scripts) listen for
ConnectionStatechanges to react accordingly (e.g., update status bar, attempt auto-joins).
- Runtime Operations:
- User commands (e.g.,
/set,/ignore) modify state throughStateManager. - Scripts can read state (
self.api.get_client_state()) and potentially modify it if the API allows. - UI components display information derived from the state (e.g., current nick, channel list).
- User commands (e.g.,
- Persistence:
StateManagerautomatically saves the entire state tostate.jsonperiodically or on significant changes (configurable).- The
/savecommand can manually trigger a save. - On shutdown,
ClientShutdownCoordinatorensures final state is saved.
Example Workflow (Simplified)
# Client starts, StateManager loads state.json
# User issues: /server MyFavoriteServer
# 1. CommandHandler parses /server, calls appropriate logic.
# 2. Logic retrieves "MyFavoriteServer" config from AppConfig.
# 3. Creates a ConnectionInfo object from this config.
# 4. Calls state_manager.set_connection_info(new_conn_info).
# - Validator for "connection_info" runs. If invalid, set_connection_info returns False.
# - If valid, state is updated. A StateChange event for "connection_info" is dispatched.
# 5. Logic calls state_manager.set_connection_state(ConnectionState.CONNECTING).
# - State is updated. A StateChange event for "connection_state" is dispatched.
# 6. UI components listening to "connection_state" update to show "Connecting...".
# 7. ConnectionOrchestrator proceeds with connection. On success:
# state_manager.set_connection_state(ConnectionState.REGISTERED)
# - Another StateChange event is dispatched. UI updates again.
Best Practices for Interacting with State
(As per README.md)
- Always use
StateManagermethods for access and modification. - Use
ConnectionInfodataclass for connection details, modifying and then setting it back viaset_connection_info(). - Check return values of
set()methods for validation success/failure. - Subscribe to specific state changes rather than polling.
- Remember
StateManagermethods are thread-safe.
This centralized and robust state management system is key to tIRC's stability and ability to maintain session information across restarts.