pytransitions / transitions

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

Initializing HierarchicalGraphMachine to Parallel State #478

Closed nickvazztau closed 3 years ago

nickvazztau commented 3 years ago

Love the library, just started using it last week!

Currently I am using a HierarchicalGraphMachine, and it looks like the 0.8.4 release of initializing to parallel states

Feature: Constructor of HierarchicalMachine now accepts substates ('A_1_c') and parallel states (['A', 'B']) as initial parameter` may be broken when adding the graph machine,

~/anaconda3/lib/python3.7/site-packages/transitions/extensions/diagrams.py in __init__(self, *args, **kwargs)
    155 
    156         _LOGGER.debug("Using graph engine %s", self.graph_cls)
--> 157         _super(GraphMachine, self).__init__(*args, **kwargs)
    158 
    159         # for backwards compatibility assign get_combined_graph to get_graph

~/anaconda3/lib/python3.7/site-packages/transitions/extensions/markup.py in __init__(self, *args, **kwargs)
     26                 self._add_markup_model(m)
     27         else:
---> 28             super(MarkupMachine, self).__init__(*args, **kwargs)
     29             self._markup['before_state_change'] = [x for x in (rep(f) for f in self.before_state_change) if x]
     30             self._markup['after_state_change'] = [x for x in (rep(f) for f in self.before_state_change) if x]

~/anaconda3/lib/python3.7/site-packages/transitions/extensions/nesting.py in __init__(self, *args, **kwargs)
    333         self._stack = []
    334         self.scoped = self
--> 335         _super(HierarchicalMachine, self).__init__(*args, **kwargs)
    336 
    337     def __call__(self, to_scope=None):

~/anaconda3/lib/python3.7/site-packages/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)
    575 
    576         if model:
--> 577             self.add_model(model)
    578 
    579     def add_model(self, model, initial=None):

~/anaconda3/lib/python3.7/site-packages/transitions/extensions/diagrams.py in add_model(self, model, initial)
    221                 raise AttributeError('Model already has a get_graph attribute. Graph retrieval cannot be bound.')
    222             setattr(mod, 'get_graph', partial(self._get_graph, mod))
--> 223             _ = mod.get_graph(title=self.title, force_new=True)  # initialises graph
    224 
    225     def add_states(self, states, on_enter=None, on_exit=None,

~/anaconda3/lib/python3.7/site-packages/transitions/extensions/diagrams.py in _get_graph(self, model, title, force_new, show_roi)
    187             try:
    188                 state = getattr(model, self.model_attribute)
--> 189                 self.model_graphs[model].set_node_style(state.name if hasattr(state, 'name') else state, 'active')
    190             except AttributeError:
    191                 _LOGGER.info("Could not set active state of diagram")

~/anaconda3/lib/python3.7/site-packages/transitions/extensions/diagrams_graphviz.py in set_node_style(self, state, style)
     44 
     45     def set_node_style(self, state, style):
---> 46         self.custom_styles['node'][state] = style
     47 
     48     def reset_styling(self):

TypeError: unhashable type: 'list'
aleneum commented 3 years ago

Hi @nickvazztau,

thank you for that fast bug report and sorry for the inconvenience. I just realized HierarchicalGraphMachine has slipped parallel testing so far. I can confirm this (and other -_-) issues when using GraphSupport.

aleneum commented 3 years ago

Hello again,

ad9a200 should fix that issue. I also fixed some styling issues with parallel states and hidden edges when using graphviz.

aleneum commented 3 years ago

Test

from transitions.extensions import HierarchicalGraphMachine as Machine

m = Machine(states=['A', 'B', {'name': 'C', 'parallel': ['1',
                                                         {'name': '2', 'children': ['foo', 'bar', 'baz'],
                                                          'transitions': [['go', 'foo', 'bar'],
                                                                          ['go', 'bar', 'baz']],
                                                          'initial': 'foo'}]}],
            transitions=[['step', ['A', 'B'], 'C'], ['undo', 'B', 'A']],
            initial='C', use_pygraphviz=False)
m.add_transition('back', 'C_1', 'B')
m.get_graph().draw('step_01.png', prog='dot')
m.back()
m.get_graph().draw('step_02.png', prog='dot')
m.step()
m.get_graph().draw('step_03.png', prog='dot')
m.go()
m.get_graph().draw('step_04.png', prog='dot')

Step1

step_01

Step2

step_02

Step3

step_03

Step4

step_04

nickvazztau commented 3 years ago

Awesome! Thank you so much for the quick response.

ad9a200 should fix that issue. I also fixed some styling issues with parallel states and hidden edges when using graphviz.

Does this mean it is included in the next release (0.8.5) on pypi?

aleneum commented 3 years ago

yes, if you need it earlier you can install transitions from the master branch via pip.

aleneum commented 3 years ago

I just released 0.8.5. If you face additional issues, let me know.