Measured on develop:
class Base(StateChart):
class c(State.Compound):
a = State(initial=True)
b = State(final=True)
go = a.to(b)
def on_enter_b(self):
pass
class Sub(Base):
pass
# AttributeError: 'State' object has no attribute '_callbacks'
add_inherited reuses the same State objects from the base class (cls.add_state(state.id, state)
over base.states), while _unpack_builders_callbacks consumes them destructively:
for state in iterate_states(cls.states):
if state._callbacks:
callbacks.update(state._callbacks)
del state._callbacks
So the base class deletes the attribute, and the subclass reads it on the shared object and
crashes. It only triggers when a nested body actually declares a callback, which is why it has
gone unnoticed.
The _callbacks bag exists because NestedStateFactory has no owning class to bind to when the
nested body is evaluated, so it hands everything back for the metaclass to flatten afterwards.
#657 removes that bag entirely, which removes this failure along with it. A narrower fix is to
stop mutating shared state: read _callbacks without deleting it, or give the subclass its own
copy of the inherited State.
Found by an adversarial review of #645, unrelated to that change.
When fixing: add a subclassing example to docs/statecharts.md or docs/models.md if none
covers inheritance with nested bodies, and add an entry to the open release notes.
Measured on
develop:add_inheritedreuses the sameStateobjects from the base class (cls.add_state(state.id, state)over
base.states), while_unpack_builders_callbacksconsumes them destructively:So the base class deletes the attribute, and the subclass reads it on the shared object and
crashes. It only triggers when a nested body actually declares a callback, which is why it has
gone unnoticed.
The
_callbacksbag exists becauseNestedStateFactoryhas no owning class to bind to when thenested body is evaluated, so it hands everything back for the metaclass to flatten afterwards.
#657 removes that bag entirely, which removes this failure along with it. A narrower fix is to
stop mutating shared state: read
_callbackswithout deleting it, or give the subclass its owncopy of the inherited
State.Found by an adversarial review of #645, unrelated to that change.
When fixing: add a subclassing example to
docs/statecharts.mdordocs/models.mdif nonecovers inheritance with nested bodies, and add an entry to the open release notes.