validators in a dict/JSON transition definition is silently dropped
Summary
create_machine_class_from_definition (the statemachine.io dict adapter) threads cond, unless, on, before, and after from each transition definition into Transition(...), but not validators. As a result, a validators entry in a dict- or JSON-defined machine is silently ignored — it never reaches the Transition, so it never runs at send().
Separately, the TransitionDict TypedDict declares validators: bool, which is the wrong type — it should be the same callback-spec union as cond/unless (str | ActionProtocol | Sequence[...]).
This matters because, per the docs, validators are the intended channel for explicit transition rejection with a reason (raise to abort), distinct from cond/unless (silent disallow). Users building machines from dict/JSON definitions currently have no way to attach that channel.
Reproduction
from statemachine.io import create_machine_class_from_definition
class Rejected(Exception):
pass
def reject(*args, **kwargs):
raise Rejected("not allowed")
SM = create_machine_class_from_definition(
"M",
states={
"s1": {"initial": True, "on": {"go": [{"target": "s2", "validators": reject}]}},
"s2": {"final": True},
},
)
sm = SM()
sm.send("go") # expected: raises Rejected; actual: succeeds, lands in s2
assert sm.current_state.id == "s1"
Expected
The validators callbacks supplied in the definition are materialized onto the Transition (which already accepts validators= in its constructor) and run at send(), exactly like cond/unless/on/before/after.
Environment
- python-statemachine
main (also reproduces on 3.1.2)
- The fix is a one-line addition to
transition_kwargs plus a TypedDict type correction.
validatorsin a dict/JSON transition definition is silently droppedSummary
create_machine_class_from_definition(thestatemachine.iodict adapter) threadscond,unless,on,before, andafterfrom each transition definition intoTransition(...), but notvalidators. As a result, avalidatorsentry in a dict- or JSON-defined machine is silently ignored — it never reaches theTransition, so it never runs atsend().Separately, the
TransitionDictTypedDict declaresvalidators: bool, which is the wrong type — it should be the same callback-spec union ascond/unless(str | ActionProtocol | Sequence[...]).This matters because, per the docs, validators are the intended channel for explicit transition rejection with a reason (raise to abort), distinct from
cond/unless(silent disallow). Users building machines from dict/JSON definitions currently have no way to attach that channel.Reproduction
Expected
The
validatorscallbacks supplied in the definition are materialized onto theTransition(which already acceptsvalidators=in its constructor) and run atsend(), exactly likecond/unless/on/before/after.Environment
main(also reproduces on 3.1.2)transition_kwargsplus a TypedDict type correction.