Transitions¶
What @state.transition(...) produces, and the objects you get back when you
look a transition method up.
Transition¶
One declared move. A decorator with several sources builds one of these per source.
| Attribute | Type | Description |
|---|---|---|
func |
callable | The undecorated method |
source |
state value | The state it leaves, or State.ANY |
target |
state value | The state it lands in, DEFAULT, or a dynamic target |
conditions |
list | The predicates, in declaration order |
custom |
dict | Your metadata. Never read by the library |
label |
str |
_label, else func.label, else func.__name__.title() |
slug |
str |
func.__name__ |
>>> transition = list(publication.Article.submit.get_transitions())[0]
>>> transition.slug, transition.label
('submit', 'Submit')
>>> transition.source, transition.target
(<ReviewState.DRAFT: 'DRAFT'>, <ReviewState.IN_REVIEW: 'IN_REVIEW'>)
| Method | Returns |
|---|---|
conditions_met(instance) |
bool — all conditions pass |
get_unmet_condition(instance) |
(condition, result) for the first failure, else None |
declared_targets() |
The states this can be known to lead to, ahead of a call |
resolve_precall_target(instance, args, kwargs) |
The target, resolving a GET_STATE |
resolve_return_value_target(result) |
The target, for a RETURN_VALUE |
declared_targets() returns a single-element list for an ordinary target, the
declared states for a restricted dynamic target, and [] for an unrestricted
one:
Transition sorts by label, so a list of them can be ordered for display with
plain sorted().
TransitionBoundMethod¶
What instance.method gives you. Callable, and answers questions without
calling.
| Member | Description |
|---|---|
__call__(*args, **kwargs) |
Runs the transition — the full lifecycle |
can_proceed(check_conditions=True) |
Whether the transition is possible now |
original(*args, **kwargs) |
Calls the undecorated function, bypassing the machine |
label |
The label of the transition available from the current state |
get_transitions() |
Every transition declared on this method |
>>> article = publication.Article('Ready', body='A body.')
>>> article.submit.can_proceed()
True
>>> article.submit.can_proceed(check_conditions=False)
True
>>> article.submit.label
'Submit'
can_proceed(check_conditions=False) asks only about the state. The difference
between the two answers is what separates "wrong stage" from
"right stage, something missing" — see
Conditions.
TransitionMethod¶
What Cls.method gives you. Not callable — there is no instance to move.
| Member | Description |
|---|---|
get_transitions() |
Every transition declared on this method |
slug |
func.__name__ |
>>> sorted(
... (str(transition.source), str(transition.target))
... for transition in publication.Article.archive.get_transitions()
... )
[('ANY', 'ReviewState.ARCHIVED')]
StateDescriptor¶
What Cls.state gives you. Proxies unknown attributes to the underlying
State, so Cls.state.propname works.
| Method | Returns |
|---|---|
get_transitions() |
{TransitionMethod: [Transition, ...]} for the whole class |
get_outgoing_transitions(state) |
Transitions leaving state |
get_available_transitions(flow, state) |
Those whose conditions pass for flow |
The result of get_transitions() is cached on the owner class under
__fsm_<propname>_transitions, and methods are discovered with
inspect.getmembers, so the order is alphabetical by method name.
The outgoing rule¶
A transition is outgoing from state when:
transition.source == state
or (transition.source == State.ANY and state not in transition.declared_targets())
The second clause stops a State.ANY transition being listed as a self-loop on
its own target. It affects listings only — the lookup performed by an actual
call is the plain one, so a wildcard still matches from its own target. See
Transitions → State.ANY.
An unrestricted dynamic target has no declared targets, so it can never be ruled out and is always listed.
The call lifecycle¶
TransitionBoundMethod.__call__ runs:
- Read the current state.
- Look up the transition for it, falling back to
State.ANY. No match →NoTransition. - Evaluate conditions in order. First falsy →
TransitionConditionsUnmet. - Resolve and set the target, unless it is a
State.RETURN_VALUE. An invalidGET_STATEresult →InvalidTargetState, before the body runs. - Call the method body.
- On
BaseException: restore the original state and re-raise. - On success: resolve a
RETURN_VALUEtarget and set it, then call@state.on_success()with the caller's keyword arguments.
Related pages¶
- Transitions — the guide
- Dynamic targets —
RETURN_VALUEandGET_STATE - Engine reference — the introspection functions