You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Event(delay=..., internal=...) are dropped when the event is declared on a class body #647
Event(..., delay=...) and Event(..., internal=...) are accepted at declaration time and then
discarded, so the resulting event has delay=0 and internal=False.
sm.light() fires immediately instead of after 500 ms, and goes to the external queue instead of
the internal one.
Cause
StateMachineMetaclass.add_from_attributes rebuilds the declared event to give it the real id,
and the new instance is constructed without the two fields:
delay and internal are declared on Event and honored everywhere downstream
(Event.put reads self.internal, the delayed-event machinery reads delay); they are simply
lost in this one rebuild.
Why the existing test does not catch it
tests/test_statechart_delayed.py::TestDelayedEvents::test_delayed_event_on_event_definition
declares light = Event(dark.to(lit), delay=50) but then triggers the machine with an event
object it builds itself:
BoundEvent(id="light", delay=50, _sm=sm)
so it never reads the declared BeaconsOfGondor.light and passes regardless of what the
metaclass did with delay. A regression test for this issue should assert on the declared
event (SM.light.delay) and drive the machine through it.
Scope
Reproduced on the current develop. Affects the top-level declaration form; the nested
(State.Compound) form drops the two fields as well, for the same reason.
add_from_attributes passes both fields through the rebuild, and the nested
(State.Compound) form preserves them too, since it now hands Events.add an Event instance
instead of a plain id string.
The regression test this issue asked for is in place:
tests/test_events.py::TestExplicitEvent::test_accept_delay_and_internal asserts on the
declared event and on the bound one.
tests/test_statechart_delayed.py::TestDelayedEvents::test_delayed_event_on_event_definition
no longer builds its own BoundEvent(id="light", delay=50); it triggers the declared event
with sm.light.put(), so it now actually exercises the bug it was named for.
Two remainders, each with its own issue:
internal is preserved on the declaration but never consumed at trigger time, because Event.__get__ does not pass it to BoundEvent and send() does not read it. So sm.light() still goes to the external queue. internal declared on an Event has no effect at trigger time #655.
Activity
fgmacedo commentedon Sep 13, 2026
Fixed by #645 (4058570 on
develop). The reproduction now prints the expected values:add_from_attributespasses both fields through the rebuild, and the nested(
State.Compound) form preserves them too, since it now handsEvents.addanEventinstanceinstead of a plain id string.
The regression test this issue asked for is in place:
tests/test_events.py::TestExplicitEvent::test_accept_delay_and_internalasserts on thedeclared event and on the bound one.
tests/test_statechart_delayed.py::TestDelayedEvents::test_delayed_event_on_event_definitionno longer builds its own
BoundEvent(id="light", delay=50); it triggers the declared eventwith
sm.light.put(), so it now actually exercises the bug it was named for.Two remainders, each with its own issue:
internalis preserved on the declaration but never consumed at trigger time, becauseEvent.__get__does not pass it toBoundEventandsend()does not read it. Sosm.light()still goes to the external queue. internal declared on an Event has no effect at trigger time #655.error_,done_state_,done_invoke_), the declareddelay,internalandnameare still dropped:_expand_event_idproduces two ids in onespace-separated string, which is the public format for "N distinct events", so each one is
rebuilt and names itself. Expanded event ids travel inside a space-separated string, so error_ never worked as a method call #654.