Skip to content

State

The descriptor that holds a machine's current state, and the namespace for the transition decorator and its target helpers.

>>> from open_fsm import State

State(states, default=None)

Argument Type Default Notes
states anything Required. Documentation only — it is not stored or validated against
default any state value None The state reported before any transition
>>> class Task:
...     status = State(['todo', 'done'], default='todo')
>>> Task().status
'todo'

Omitting states is a TypeError, even though the value is unused:

>>> State(default='todo')
Traceback (most recent call last):
    ...
TypeError: State.__init__() missing 1 required positional argument: 'states'

Attributes

Name Description
State.ANY Wildcard source, matched when no exact source is registered
propname The instance attribute the value is stored under, '__fsm_' + name
>>> Task.status.propname
'__fsm_status'

Descriptor behaviour

Access Result
instance.state The current state value, via get()
Cls.state A StateDescriptor — see Transitions
instance.state = value Always raises AttributeError
>>> task = Task()
>>> task.status = 'done'
Traceback (most recent call last):
    ...
AttributeError: Direct state modification is not allowed

Methods

get(instance)

Returns the current state. With a @state.getter() installed, the getter's value is used, falling back to default when it is None or '' and default is truthy. Without a getter, reads instance.__fsm_<name>, defaulting to default.

See The empty-value rule.

set(instance, value)

Writes the state, through the @state.setter() if one is installed. Called by the transition machinery; calling it yourself bypasses the machine.

transition_succeed(instance, transition, source, target, **kwargs)

Invokes the @state.on_success() hook, if one is installed. Called after a transition commits.

Decorators

transition(source, target=DEFAULT, label=None, conditions=None, custom=None)

Registers a method as a transition. See Transitions.

Argument Notes
source One state, or a list/tuple/set of them, or State.ANY
target Omit for no state change. None raises ValueError
label Falls back to func.__name__.title()
conditions A sequence of callables taking the instance
custom A dict, stored on the transition and never read by the library

super()

Wraps an inherited transition, reusing its source and target. Raises ValueError('Base transition not found') if no base declares a transition method of that name. See Inheritance.

getter() / setter() / on_success()

Install the accessors and the commit hook. See Binding state to storage.

>>> class Stored:
...     state = State(['new', 'done'], default='new')
...     def __init__(self, row):
...         self.row = row
...     @state.getter()
...     def _read(self):
...         return self.row['state']
...     @state.setter()
...     def _write(self, value):
...         self.row['state'] = value
...     @state.on_success()
...     def _save(self, transition, source, target, **kwargs):
...         self.row['saved'] = True
...     @state.transition(source='new', target='done')
...     def finish(self):
...         """Finish the job."""
>>> row = {'state': ''}
>>> stored = Stored(row)
>>> stored.state
'new'
>>> stored.finish()
>>> row
{'state': 'done', 'saved': True}

The on_success signature is (self, transition, source, target, **kwargs), where kwargs are the keyword arguments the caller passed to the transition.

State.CONDITION(is_true, unmet='')

A boolean-like result carrying the reason a condition refused.

Member Description
is_true The boolean result
unmet The message, as passed
message unmet when it refused, '' when it passed
__bool__ is_true
>>> State.CONDITION(False, unmet='too short').message
'too short'
>>> State.CONDITION(True, unmet='too short').message
''

See Conditions.

State.RETURN_VALUE(*allowed_states)

A target resolved from the transition method's return value, after the body runs. Stores allowed_states as a tuple; empty means any value is accepted. Raises InvalidTargetState otherwise.

State.GET_STATE(func, states=None)

A target computed by func(instance, *args, **kwargs), before the body runs. Stores states as a list, defaulting to [], which means any value is accepted. Raises InvalidTargetState otherwise.

Both are covered in full in Dynamic targets.