Source code for statemachine.states

from enum import Enum
from typing import Dict  # deprecated since 3.9: https://peps.python.org/pep-0585/
from typing import Type

from .state import State
from .utils import ensure_iterable

EnumType = Type[Enum]


[docs] class States: """ A class representing a collection of :ref:`State` objects. Helps creating :ref:`StateMachine`'s :ref:`state` definitions from other sources, like an ``Enum`` class, using :meth:`States.from_enum`. >>> from statemachine import StateMachine >>> class SM(StateMachine): ... ... states = States({ ... name: State(initial=idx == 0) for idx, name in enumerate(["initial", "final"]) ... }) ... ... finish = states.initial.to(states.final) And states can be used as usual. >>> sm = SM() >>> sm.send("finish") >>> sm.current_state.id 'final' """
[docs] def __init__(self, states: "Dict[str, State] | None" = None) -> None: """ Initializes a new instance of the States class. Args: states: A dictionary mapping keys as ``State.id`` and values :ref:`state` instances. Returns: None. """ self._states: Dict[str, State] = states if states is not None else {}
def __repr__(self): return f"{list(self)}" def __eq__(self, other): return list(self) == list(other) def __getattr__(self, name: str): if name in self._states: return self._states[name] raise AttributeError(f"{name} not found in {self.__class__.__name__}") def __len__(self): return len(self._states) def __iter__(self): return iter(self._states.values()) def append(self, state): self._states[state.id] = state
[docs] def items(self): """ Returns a view object of the states, with pairs of ``(State.id, State)``. Args: None. Returns: A view object of the items in the the instance. """ return self._states.items()
[docs] @classmethod def from_enum(cls, enum_type: EnumType, initial: Enum, final=None): """ Creates a new instance of the ``States`` class from an enumeration. Consider an ``Enum`` type that declares our expected states: >>> class Status(Enum): ... pending = 1 ... completed = 2 A :ref:`StateMachine` that uses this enum can be declared as follows: >>> from statemachine import StateMachine >>> class ApprovalMachine(StateMachine): ... ... _ = States.from_enum(Status, initial=Status.pending, final=Status.completed) ... ... finish = _.pending.to(_.completed) ... ... def on_enter_completed(self): ... print("Completed!") .. note:: Given that you assign the response of ``States.from_enum`` to a class level variable on your :ref:`StateMachine` you're good to go, you can use any name to assign this response, on this example we used ``_`` to indicate that the name does not matter. The variable of type :ref:`States (class)` will be inspected by the metaclass and the inner :ref:`State` instances assigned to the state machine. Everything else is similar, the ``Enum`` is only used to declare the :ref:`State` instances. >>> sm = ApprovalMachine() >>> sm.pending.is_active True >>> sm.send("finish") Completed! >>> sm.completed.is_active True >>> sm.current_state_value 2 Args: enum_type: An enumeration containing the states of the machine. initial: The initial state of the machine. final: A set of final states of the machine. Returns: A new instance of the :ref:`States (class)`. """ final_set = set(ensure_iterable(final)) return cls( { e.name: State(value=e.value, initial=e is initial, final=e in final_set) for e in enum_type } )