Source code for sextile.state

"""What a service holds while it is running, and the typed keys into it.

What a service opens for the life of the process -- an archive, an HTTP client,
an index -- is held on the application and reached from a page. The framework
cannot know what any service puts there, so the store is untyped; a `StateKey`
states the type once, on the key, and carries it to both ends:

    DB = StateKey[Connection]("db")

    app.state[DB] = connect(...)          # in the lifespan
    conn = request.state[DB]              # in a page, typed Connection

A key's identity is the key, not its name: two `StateKey`s with the same name
are different keys and do not collide. The name is for `repr` and the message
when a key is read that was never written.
"""

from typing import Any, Generic, Protocol, TypeVar, cast

__all__ = [
    "State",
    "StateKey",
    "StateReader",
]

#: Old-style, invariant `TypeVar` on purpose. A PEP 695 `class StateKey[T]`
#: gets its variance inferred, and with `T` used only as the key's own tag mypy
#: infers it loosely enough that `state[key_of_int] = "a string"` type-checks --
#: which is the one thing this class exists to stop. The invariant `Generic[T]`
#: pins `T` from the key so the value is checked against it, as `pytest.Stash`
#: does for the same reason.
_T = TypeVar("_T")


[docs] class StateKey(Generic[_T]): # noqa: UP046 -- invariance, see _T above """A typed key into a service's state. Attributes: name: What the key is called, used in `repr` and in the error a missing key raises. It is not how the value is stored: the key's own identity is, so two keys of the same name are distinct. """ def __init__(self, name: str) -> None: self.name = name def __repr__(self) -> str: return f"StateKey({self.name!r})"
[docs] class StateReader(Protocol): """The read-only view of a service's state that a page is given. A page reads what the service holds and never writes it: shared state changed from a page would reach every other caller at once. This is that guarantee said in the types -- there is no `__setitem__` here. """ def __getitem__(self, key: StateKey[_T]) -> _T: ... def get(self, key: StateKey[_T], default: _T | None = None) -> _T | None: ... def __contains__(self, key: StateKey[Any]) -> bool: ...
[docs] class State: """What a service holds while it is running, keyed by `StateKey`. Writable, and held on the application; a page is given the read-only `StateReader` view of it instead. """ def __init__(self) -> None: self._held: dict[StateKey[Any], object] = {} def __setitem__(self, key: StateKey[_T], value: _T) -> None: self._held[key] = value def __getitem__(self, key: StateKey[_T]) -> _T: try: held = self._held[key] except KeyError: raise KeyError( f"the service is not holding {key.name!r}; has it started?" ) from None return cast("_T", held) def get(self, key: StateKey[_T], default: _T | None = None) -> _T | None: return cast("_T | None", self._held.get(key, default)) def __contains__(self, key: StateKey[Any]) -> bool: return key in self._held
[docs] def clear(self) -> None: """Forget everything held, as the service stops.""" self._held.clear()