0

My question is actually pretty simple, but I am not well versed in pytransition code to find the answer by myself:

Is is possible to use AsyncGraphMachine with async on_enter callbacks only.

In the documentation: https://github.com/pytransitions/transitions#-using-async-callbacks it is stated that:

If you are using Python 3.7 or later, you can use AsyncMachine to work with asynchronous callbacks. You can mix synchronous and asynchronous callbacks if you like but this may have undesired side effects. Note that events need to be awaited and the event loop must also be handled by you.

I am using python > 3.7, and I cannot make my samples to work:

from transitions.extensions.asyncio import AsyncMachine
import asyncio
import time
from transitions import State


class AsyncModel:

    async def do_async(self):
        print("Do async start")
        await asyncio.sleep(5)
        print("Do async end")

    def do_sync(self):
        print("Do async start")
        time.sleep(5)
        print("Do async end")

transitions = [dict(trigger="start", source="*", dest="initialization"),
               dict(trigger="finish", source="initialization", dest="final")]
states = [State(name="initialization", on_enter=["do_async", "do_sync"]),
          State(name="final", on_enter=["do_async", "do_sync"])]

model = AsyncModel()
machine = AsyncMachine(model, states=states, transitions=transitions)

asyncio.get_event_loop().run_until_complete(model.start())

The actual error is:

    /home/user/.local/lib/python3.8/site-packages/transitions/core.py:128: RuntimeWarning: coroutine 'AsyncMachine.callbacks' was never awaited
      event_data.machine.callbacks(self.on_enter, event_data)       RuntimeWarning: Enable tracemalloc to get the object allocation traceback       Traceback (most recent call last):
      File "./test.py", line 27, in <module>
        asyncio.get_event_loop().run_until_complete(model.start())
      File "/usr/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete
        return future.result()
      File "/home/user/.local/lib/python3.8/site-packages/transitions/extensions/asyncio.py", line 177, in trigger
        return await self.machine.process_context(func, _model)
      File "/home/user/.local/lib/python3.8/site-packages/transitions/extensions/asyncio.py", line 409, in process_context
        res = await self._process(func, model)
      File "/home/user/.local/lib/python3.8/site-packages/transitions/extensions/asyncio.py", line 443, in _process
        return await trigger()
      File "/home/user/.local/lib/python3.8/site-packages/transitions/extensions/asyncio.py", line 192, in _trigger
        return await self._process(event_data)
      File "/home/user/.local/lib/python3.8/site-packages/transitions/extensions/asyncio.py", line 201, in _process
        if await trans.execute(event_data):
      File "/home/user/.local/lib/python3.8/site-packages/transitions/extensions/asyncio.py", line 130, in execute
        await self._change_state(event_data)
      File "/home/user/.local/lib/python3.8/site-packages/transitions/extensions/asyncio.py", line 145, in _change_state
        await event_data.machine.get_state(self.dest).enter(event_data)         TypeError: object NoneType can't be used in 'await' expression

However, on_enter type of function are callbacks, according to the documentation itself: https://github.com/pytransitions/transitions#state-callbacks

So I wonder what is going on here.

Thank you in advance for your help

EDIT: ok the problem might even be somewhere I wouldn't even have considered:

from transitions.extensions.asyncio import AsyncMachine
import asyncio
import time
from transitions import State


class AsyncModel:

    async def do_async(self):
        print("Do async start")
        await asyncio.sleep(5)
        print("Do async end")

    def do_sync(self):
        print("Do sync start")
        time.sleep(5)
        print("Do sync end")

transitions = [dict(trigger="start", source="initialization", dest="final", before=["do_async", "do_sync"], after=["do_sync"], prepare=["do_sync"])]
states = [State(name="initialization"),
          State(name="final")]

model = AsyncModel()
machine = AsyncMachine(model, states=["initialization", "final"], transitions=transitions, initial="initialization")

asyncio.get_event_loop().run_until_complete(model.start())

This is working, however this is failing:

from transitions.extensions.asyncio import AsyncMachine
import asyncio
import time
from transitions import State


class AsyncModel:

    async def do_async(self):
        print("Do async start")
        await asyncio.sleep(5)
        print("Do async end")

    def do_sync(self):
        print("Do sync start")
        time.sleep(5)
        print("Do sync end")

transitions = [dict(trigger="start", source="initialization", dest="final", before=["do_async", "do_sync"], after=["do_sync"], prepare=["do_sync"])]
states = [State(name="initialization"),
          State(name="final")]

model = AsyncModel()
machine = AsyncMachine(model, states=states, transitions=transitions, initial="initialization")

asyncio.get_event_loop().run_until_complete(model.start())

Looks like vanilla states from transitions are not compatible with async machines, you should rather use "from transitions.extensions.asyncio import AsyncState" instead

Tobbey
  • 469
  • 1
  • 4
  • 18

1 Answers1

0

Ok so actually I found the answer by trial and error, my issue was that I was using State instead of AsyncState

Tobbey
  • 469
  • 1
  • 4
  • 18