python-statemachine icon indicating copy to clipboard operation
python-statemachine copied to clipboard

Async conditions are not awaited when using certain condition expressions

Open nimobeeren opened this issue 4 months ago • 1 comments

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.

nimobeeren avatar Aug 27 '25 07:08 nimobeeren

I was able to reproduce the issue. Thanks for reporting this.

fgmacedo avatar Sep 15 '25 13:09 fgmacedo