from __future__ import annotations from copy import copy, deepcopy from threading import RLock from typing import TYPE_CHECKING, Any, Callable, Generator, Iterable, Iterator, Mapping, MutableMapping if TYPE_CHECKING: from typing_extensions import Self __all__ = ("ImmutableState", "State") class ImmutableState(Mapping[str, Any]): """An object meant to store arbitrary state. It can be accessed using dot notation while exposing dict like functionalities. """ __slots__ = ( "_state", "_deep_copy", ) _state: dict[str, Any] def __init__( self, state: ImmutableState | Mapping[str, Any] | Iterable[tuple[str, Any]], deep_copy: bool = True ) -> None: """Initialize an ``ImmutableState`` instance. Args: state: An object to initialize the state from. Can be a dict, an instance of :class:`ImmutableState`, or a tuple of key value paris. deep_copy: Whether to 'deepcopy' the passed in state. Examples: .. code-block:: python from litestar.datastructures import ImmutableState state_dict = {"first": 1, "second": 2, "third": 3, "fourth": 4} state = ImmutableState(state_dict) # state implements the Mapping type: assert len(state) == 3 assert "first" in state assert not "fourth" in state assert state["first"] == 1 assert [(k, v) for k, v in state.items()] == [("first", 1), ("second", 2), ("third", 3)] # state implements __bool__ assert state # state is true when it has values. assert not State() # state is empty when it has no values. # it has a 'dict' method to retrieve a shallow copy of the underlying dict inner_dict = state.dict() assert inner_dict == state_dict # you can also retrieve a mutable State by calling 'mutable_copy' mutable_state = state.mutable_copy() del state["first"] assert "first" not in state """ if isinstance(state, ImmutableState): state = state._state if not isinstance(state, dict) and isinstance(state, Iterable): state = dict(state) super().__setattr__("_deep_copy", deep_copy) super().__setattr__("_state", deepcopy(state) if deep_copy else state) def __bool__(self) -> bool: """Return a boolean indicating whether the wrapped dict instance has values.""" return bool(self._state) def __getitem__(self, key: str) -> Any: """Get the value for the corresponding key from the wrapped state object using subscription notation. Args: key: Key to access. Raises: KeyError Returns: A value from the wrapped state instance. """ return self._state[key] def __iter__(self) -> Iterator[str]: """Return an iterator iterating the wrapped state dict. Returns: An iterator of strings """ return iter(self._state) def __len__(self) -> int: """Return length of the wrapped state dict. Returns: An integer """ return len(self._state) def __getattr__(self, key: str) -> Any: """Get the value for the corresponding key from the wrapped state object using attribute notation. Args: key: Key to retrieve Raises: AttributeError: if the given attribute is not set. Returns: The retrieved value """ try: return self._state[key] except KeyError as e: raise AttributeError from e def __copy__(self) -> Self: """Return a shallow copy of the given state object. Customizes how the builtin "copy" function will work. """ return self.__class__(self._state, deep_copy=self._deep_copy) # pyright: ignore def mutable_copy(self) -> State: """Return a mutable copy of the state object. Returns: A ``State`` """ return State(self._state, deep_copy=self._deep_copy) def dict(self) -> dict[str, Any]: """Return a shallow copy of the wrapped dict. Returns: A dict """ return copy(self._state) @classmethod def __get_validators__( cls, ) -> Generator[Callable[[ImmutableState | dict[str, Any] | Iterable[tuple[str, Any]]], ImmutableState], None, None]: # type: ignore[valid-type] """Pydantic compatible method to allow custom parsing of state instances in a SignatureModel.""" yield cls.validate @classmethod def validate(cls, value: ImmutableState | dict[str, Any] | Iterable[tuple[str, Any]]) -> Self: # type: ignore[valid-type] """Parse a value and instantiate state inside a SignatureModel. This allows us to use custom subclasses of state, as well as allows users to decide whether state is mutable or immutable. Args: value: The value from which to initialize the state instance. Returns: An ImmutableState instance """ deep_copy = value._deep_copy if isinstance(value, ImmutableState) else False return cls(value, deep_copy=deep_copy) class State(ImmutableState, MutableMapping[str, Any]): """An object meant to store arbitrary state. It can be accessed using dot notation while exposing dict like functionalities. """ __slots__ = ("_lock",) _lock: RLock def __init__( self, state: ImmutableState | Mapping[str, Any] | Iterable[tuple[str, Any]] | None = None, deep_copy: bool = False, ) -> None: """Initialize a ``State`` instance with an optional value. Args: state: An object to initialize the state from. Can be a dict, an instance of 'ImmutableState', or a tuple of key value paris. deep_copy: Whether to 'deepcopy' the passed in state. .. code-block:: python :caption: Examples from litestar.datastructures import State state_dict = {"first": 1, "second": 2, "third": 3, "fourth": 4} state = State(state_dict) # state can be accessed using '.' notation assert state.fourth == 4 del state.fourth # state implements the Mapping type: assert len(state) == 3 assert "first" in state assert not "fourth" in state assert state["first"] == 1 assert [(k, v) for k, v in state.items()] == [("first", 1), ("second", 2), ("third", 3)] state["fourth"] = 4 assert "fourth" in state del state["fourth"] # state implements __bool__ assert state # state is true when it has values. assert not State() # state is empty when it has no values. # it has shallow copy copied_state = state.copy() del copied_state.first assert state.first # it has a 'dict' method to retrieve a shallow copy of the underlying dict inner_dict = state.dict() assert inner_dict == state_dict # you can get an immutable copy of the state by calling 'immutable_immutable_copy' immutable_copy = state.immutable_copy() del immutable_copy.first # raises AttributeError """ super().__init__(state if state is not None else {}, deep_copy=deep_copy) super().__setattr__("_lock", RLock()) def __delitem__(self, key: str) -> None: """Delete the value from the key from the wrapped state object using subscription notation. Args: key: Key to delete Raises: KeyError: if the given attribute is not set. Returns: None """ with self._lock: del self._state[key] def __setitem__(self, key: str, value: Any) -> None: """Set an item in the state using subscription notation. Args: key: Key to set. value: Value to set. Returns: None """ with self._lock: self._state[key] = value def __setattr__(self, key: str, value: Any) -> None: """Set an item in the state using attribute notation. Args: key: Key to set. value: Value to set. Returns: None """ with self._lock: self._state[key] = value def __delattr__(self, key: str) -> None: """Delete the value from the key from the wrapped state object using attribute notation. Args: key: Key to delete Raises: AttributeError: if the given attribute is not set. Returns: None """ try: with self._lock: del self._state[key] except KeyError as e: raise AttributeError from e def copy(self) -> Self: """Return a shallow copy of the state object. Returns: A ``State`` """ return self.__class__(self.dict(), deep_copy=self._deep_copy) # pyright: ignore def immutable_copy(self) -> ImmutableState: """Return a shallow copy of the state object, setting it to be frozen. Returns: A ``State`` """ return ImmutableState(self, deep_copy=self._deep_copy)