pytransitions / transitions

A lightweight, object-oriented finite state machine implementation in Python with many extensions
MIT License
5.49k stars 525 forks source link

State changes in a hierarchical model don't update the state on the nested component models #596

Open translunar opened 1 year ago

translunar commented 1 year ago

Suppose I have a hierarchical state machine and I transition into a nested state (suppose caffeinated, which is defined as its own model). The state on the hierarchical state machine correctly changes to caffeinated_dithering, but the state on the nested machine does not change to dithering; it remains at whatever the initial state was.

Is there a way to force the nested model's state to change? I considered using on_enter in the outer model, but then realized that won't work because that transition already has an on_enter defined (which it correctly copies from the nested model).

Perhaps this is a feature request rather than a bug. I'm treating it as a bug because it violates the principle of least surprise. I expect the nested model's state to change when the outer model transitions between states that originate in the nested model.

translunar commented 1 year ago

I have added an MWE and am tentatively working on a solution.

translunar commented 1 year ago

This is really tricky because when we define nested states, we pass a machine, which has a model class affiliated but not a class instance. The nested instance is the thing whose state we really want to update.

The easiest way I can think of to accomplish this is to allow child states to be defined with {"name": "A", "children": child_model} instead of "children": child_model.machine. Unfortunately, that locks us into always having a machine instance variable on our models. It also links a model class to a particular nested model instance, whereas a model class should probably linked to a nested model class and a model instance to a nested model instance.

Alternatively, we could add a set_child_model_instance method to NestedMachine and stick it in a dict on the machine, where the instance only gets updated if that dict entry has been set. That's probably the cleanest design-wise, but still is kind of a hack.