-
-
Notifications
You must be signed in to change notification settings - Fork 99
Description
When a statemachine has at least one async
condition callback and this condition is used in a transition cond
parameter using certain condition expressions (see below), Python warns that a coroutine was never awaited, and may cause transition conditions to be evaluated incorrectly.
Minimal repro:
import asyncio
from statemachine import State, StateMachine
class AsyncConditionBug(StateMachine):
init = State(initial=True)
go = init.to.itself(cond="not cond_false")
async def cond_false(self):
return False
async def main():
sm = AsyncConditionBug()
await sm.send("go") # type: ignore
if __name__ == "__main__":
asyncio.run(main())
yields the following output (scroll to the right to see "coroutine was never awaited" warning):
/Users/nimo.beeren/Development/io/efteling/sprookjesboom/.venv/lib/python3.13/site-packages/statemachine/spec_parser.py:37: RuntimeWarning: coroutine 'callable_method.<locals>.signature_adapter' was never awaited
return not predicate(*args, **kwargs)
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
Traceback (most recent call last):
File "/Users/nimo.beeren/Development/io/efteling/sprookjesboom/repro.py", line 25, in <module>
asyncio.run(main())
~~~~~~~~~~~^^^^^^^^
File "/Users/nimo.beeren/.local/share/uv/python/cpython-3.13.1-macos-aarch64-none/lib/python3.13/asyncio/runners.py", line 194, in run
return runner.run(main)
~~~~~~~~~~^^^^^^
File "/Users/nimo.beeren/.local/share/uv/python/cpython-3.13.1-macos-aarch64-none/lib/python3.13/asyncio/runners.py", line 118, in run
return self._loop.run_until_complete(task)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
File "/Users/nimo.beeren/.local/share/uv/python/cpython-3.13.1-macos-aarch64-none/lib/python3.13/asyncio/base_events.py", line 720, in run_until_complete
return future.result()
~~~~~~~~~~~~~^^
File "/Users/nimo.beeren/Development/io/efteling/sprookjesboom/repro.py", line 21, in main
await sm.send("go") # type: ignore
~~~~~~~^^^^^^
File "/Users/nimo.beeren/Development/io/efteling/sprookjesboom/.venv/lib/python3.13/site-packages/statemachine/statemachine.py", line 312, in send
result = event_instance(*args, **kwargs)
File "/Users/nimo.beeren/Development/io/efteling/sprookjesboom/.venv/lib/python3.13/site-packages/statemachine/event.py", line 133, in __call__
result = machine._processing_loop()
File "/Users/nimo.beeren/Development/io/efteling/sprookjesboom/.venv/lib/python3.13/site-packages/statemachine/statemachine.py", line 112, in _processing_loop
return self._engine.processing_loop()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
File "/Users/nimo.beeren/Development/io/efteling/sprookjesboom/.venv/lib/python3.13/site-packages/statemachine/engines/sync.py", line 67, in processing_loop
result = self._trigger(trigger_data)
File "/Users/nimo.beeren/Development/io/efteling/sprookjesboom/.venv/lib/python3.13/site-packages/statemachine/engines/sync.py", line 98, in _trigger
raise TransitionNotAllowed(trigger_data.event, state)
statemachine.exceptions.TransitionNotAllowed: Can't go when in Init.
It seems like the coroutine returned by cond_false
is not awaited, but is evaluated as a truthy value, which disallows the transition. Removing async
from def cond_false
fixes it.
Adding a second condition lets us explore more cases:
async def cond_true(self):
return True
Changing the cond
parameter of the go
transition to the following values:
- ❌
not cond_false
: as above ⚠️ cond_true and cond_true
: prints "coroutine was never awaited" warning but the transition is correctly allowed- ✅
cond_true or cond_false
: no warning or error, seems correct
It seems like not
and and
are triggering the issue while or
is fine.