python-3.xasynchronousstate-machinehierarchicalpytransitions

Pytransitions: How can we accomplish a statemachine which posses the features of Hierarchical Machine and AsyncMachine in a single state machine?


I want to build a stat machine such that it can posses features of both HierarchicalMachine and AsyncMachine. I tried this code but Hierarchical and Async are not working simultaneously.

`

from transitions.extensions.markup import MarkupMachine
from transitions.extensions.factory import HierarchicalMachine
from transitions.extensions.asyncio import AsyncMachine
QUEUED = False

class Unhealthy(HierarchicalMachine, AsyncMachine):
    def __init__(self):
        states = [{"name":'aborted', "on_enter":[]},
                    {"name":'clearancetimeouterr', "on_enter":[]},
                    {"name":"awaitingclearanceerr", 'on_enter':[]},
                    {"name":"cleared", 'on_enter':[]}
                    ]
        transitions = [{"trigger":"abort", "source":"aborted", "dest":"awaitingclearanceerr"},
                        {"trigger":"awaitingclearanceerr", "source":"clearancetimeout", "dest":"awaitingclearanceerr"},
                        {"trigger":"cleared", "source":"awaitingclearanceerr", "dest":"cleared"}]
        super().__init__(states=states, transitions=transitions, initial="awaitingclearanceerr", queued=QUEUED)
        

class Healthy(HierarchicalMachine, AsyncMachine):
    def __init__(self):
        unhealthy = Unhealthy()
        states = [{"name":'idle', 'on_enter':[]},
                    {"name":"busy", 'on_enter':[]},
                    {"name":"done", 'on_enter':[]}]

        transitions = [{'trigger':'start', 'source':'idle', 'dest':'busy'},
                        {"trigger":"done", "source":"busy", "dest":"done"},
                        {"trigger":"idle", "source":"awaiting_clearance", "dest":"idle"}]
        super().__init__(states=states, transitions=transitions, initial="idle", queued=QUEUED)



class StateMachine(HierarchicalMachine, MarkupMachine, AsyncMachine):
    def __init__(self):
        unhealthy= Unhealthy()
        healthy = Healthy()
        states = [{'name':"idle"}, {"name":'healthy', 'children':healthy}, {"name":"unhealthy", "children":unhealthy}]
        super().__init__(states=states, initial="idle", queued=QUEUED)
        self.add_transition("start_machine", "idle", "healthy")
        self.add_transition('abort', 'healthy', 'unhealthy')

I want something like that but HierarchicalMachine and AsyncMachine are not working together. And giving the following error:RuntimeError: AsyncMachine should not call Machine._process. Use Machine._process_async instead.'


Solution

  • The extensions section of transitions documentation mentions the following:

    There are two mechanisms to retrieve a state machine instance with the desired features enabled. The first approach makes use of the convenience factory with the four parameters graph, nested, locked or asyncio set to True if the feature is required.

    from transitions.extensions import MachineFactory
    
    AsyncHSM = MachineFactory.get_predefined(nested=True, asyncio=True)
    
    machine = AsyncHSM(states=['A', 'B'], ...)
    
    class MyHSM(ASyncHSM):
       ...
    

    This approach targets experimental use since in this case the underlying classes do not have to be known. However, classes can also be directly imported from transitions.extensions. The naming scheme is as follows:

    Diagrams Nested Locked Asyncio
    Machine
    GraphMachine
    HierarchicalMachine
    LockedMachine
    HierarchicalGraphMachine
    LockedGraphMachine
    LockedHierarchicalMachine
    LockedHierarchicalGraphMachine
    AsyncMachine
    AsyncGraphMachine
    HierarchicalAsyncMachine
    HierarchicalAsyncGraphMachine

    So I guess this is what you are looking for:

    from transitions.extensions import HierarchicalAsyncMachine
    # in case you want markup/graphviz support
    from transitions.extensions import HierarchicalAsyncGraphMachine
    

    In case you want to 'mix' your own machine class, have a look at factory.py to get an idea about how the subclasses are defined. Pay attention to the order of inheritance. Mixing this up might cause undesired effects.