pytransitions / transitions

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

HierarchicalMachine cannot add state of type State. #414

Closed xiaohuihui1024 closed 4 years ago

xiaohuihui1024 commented 4 years ago

Hi @aleneum Using: Windows 10 64 bit Python 3.7.4 (Anaconda) transitions 0.8.1 Graphviz 2.38.0 (binary) graphviz 0.13.2 (python package)

I found a issue that HierarchicalMachine cannot add state of type State.

from transitions.extensions.factory import HierarchicalGraphMachine, HierarchicalMachine
from transitions import Machine
class Matter(object):
    def say_hello(self): print("hello, new state!")
    def say_goodbye(self): print("goodbye, old state!")

lump = Matter()

states = [
    State(name='solid', on_exit=['say_goodbye']), 
    'liquid',
    { 'name': 'gas' }
    ]
m1 = HierarchicalMachine(lump, states=states)

'solid' state is an instance of the State class

The error output is as follows

ValueError                                Traceback (most recent call last)
<ipython-input-18-f8fcfa6ee343> in <module>
     20 ]
     21 # machine = GraphMachine(states=states, transitions=transitions, initial='Waiting for Sending SignIn Package',**extra_args)
---> 22 machine1 = HierarchicalMachine(states=states, transitions=transitions, initial='Waiting for Sending SignIn Package')

C:\Anaconda\Lib\site-packages\transitions-0.8.1-py3.7.egg\transitions\extensions\nesting.py in __init__(self, *args, **kwargs)
    337         self._stack = []
    338         self.scoped = self
--> 339         _super(HierarchicalMachine, self).__init__(*args, **kwargs)
    340 
    341     def __call__(self, to_scope=None):

C:\Anaconda\Lib\site-packages\transitions-0.8.1-py3.7.egg\transitions\core.py in __init__(self, model, states, initial, transitions, send_event, auto_transitions, ordered_transitions, ignore_invalid_triggers, before_state_change, after_state_change, name, queued, prepare_event, finalize_event, model_attribute, **kwargs)
    560 
    561         if states is not None:
--> 562             self.add_states(states)
    563 
    564         if initial is not None:

C:\Anaconda\Lib\site-packages\transitions-0.8.1-py3.7.egg\transitions\extensions\nesting.py in add_states(self, states, on_enter, on_exit, ignore_invalid_triggers, **kwargs)
    498                     self.scoped.initial = state.initial
    499             else:
--> 500                 raise ValueError("Cannot add state of type {0}.".format(type(state).__name__))
    501 
    502     def add_transition(self, trigger, source, dest, conditions=None,

ValueError: Cannot add state of type State.

Using HierarchicalGraphMachine will have the same issue

m2 = HierarchicalGraphMachine(lump, states=states)

But using Machine can work normally

machine = Machine(lump, states=states)
aleneum commented 4 years ago

Hi @xiaohuihui1024, State is a rather simple class which is suitable for 'flat' state machines based on Machine. However, as also stated in the readme:

HierarchicalMachine requires your custom state to be an instance of NestedState (State is not sufficient)

State instances passed to HierarchicalMachine must be NestedState or derived classes. The code above works fine with:

from transitions.extensions.factory import HierarchicalMachine
from transitions.extensions.nesting import NestedState

from transitions import Machine
class Matter(object):
    def say_hello(self): print("hello, new state!")
    def say_goodbye(self): print("goodbye, old state!")

lump = Matter()

states = [
    NestedState(name='solid', on_exit=['say_goodbye']),
    'liquid',
    { 'name': 'gas' }
    ]
m1 = HierarchicalMachine(lump, states=states)

But since you are not the first one attempting to use simple State objects in HierarchicalMachine I will try to improve the error message so that this issues becomes a bit clearer. I will also add this fact explicitely in the documentation.

xiaohuihui1024 commented 4 years ago

Thank you for the quick response. I may not have noticed this detail in the 0.8.0 version of the document Your answer is perfect, it solves my problem, and also provides a friendly reminder for people who may encounter this problem in the future.

aleneum commented 4 years ago

Yeah, it was definitely not prominent enough. This is one of those things that is hard to spot as a developer (or a user "too" familiar with the module). I will close this since this seems to be solved. Feel free to comment when there is more to add. I will reopen the issue if necessary. Thanks again for taking the time to bring this to our attention.