gawel / aiocron

Crontabs for asyncio
MIT License
338 stars 20 forks source link

InvalidStateError when cancelling #19

Closed vdwees closed 2 years ago

vdwees commented 2 years ago

When I cancel an asyncio.Task that is awaiting an aiocron next(), sometimes I see this error. Any ideas?

ERROR:asyncio:Exception in callback <bound method Cron.set_result of <Cron * * * * * */2 <function null_callback at 0x7f23a49f2700>>>
handle: <Handle Cron.set_result>
Traceback (most recent call last):
  File "uvloop/cbhandles.pyx", line 63, in uvloop.loop.Handle._run
  File "venv/lib/python3.9/site-packages/aiocron/__init__.py", line 97, in set_result
    self.future.set_result(result)
asyncio.exceptions.InvalidStateError: invalid state
gawel commented 2 years ago

Definitely a bug. I guess self.future is canceled when we try to set it. Do you have a snippet to reproduce the problem ?

vdwees commented 2 years ago

Its not every time, but on my machine if I run this a few times it does show up.


import asyncio
from datetime import timezone

import aiocron

async def wait_for():
    try:
        await aiocron.crontab("* * * * * */1", tz=timezone.utc).next()
    except asyncio.CancelledError:
        print("cancelled")
    else:
        print("done")

async def main():
    task = asyncio.create_task(wait_for())
    await asyncio.sleep(0.5)
    task.cancel()
    await task
    await asyncio.sleep(1.0)

asyncio.run(main())
vdwees commented 2 years ago

I've added an explicit call to stop() on receiving the cancelled error, which seems like a reasonable workaround.