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