try:
async with asyncio.timeout(delay):
await event.wait()
except TimeoutError:
return "No event"
return "Event!"
Unfortunately, it is not either timeout or event as I originally assumed. When the event and the timeout occur almost in the same time, we could have both the timeout and the event. The code below proved it. But as I said, it is timing sensitive.
try:
async with asyncio.timeout(delay):
await event.wait()
except TimeoutError:
assert not event.is_set() # <-- not always true!
return "No event"
...
An await cannot both succeed and fail. It looks to me like other tasks can be run during timeout handling in the timeout context manager. I don’t know, I just cannot explain the event state change otherwise.
Anyway, in my case it introduced an ugly bug. This is how I corrected it:
try:
async with asyncio.timeout(delay):
await event.wait()
except TimeoutError:
pass
return "Event!" if event.is_set() else "No event"
I decided to post this because I would like to know if it can be considered a “gotcha” that should be somehow addressed, e.g. by enhancing the doumentation?
While there is indeed nothing preventing the event to be set after you’ve awaited it that shouldn’t be possible without some await between the wait and the check.
Is there an await in the real code?
Because otherwise it should only really be possible if the event is set by another thread which is not supported as Event like most asyncio structures are not thread safe.
Or maybe (I would have to check the code) it could be that we hit the TimeoutError even when the event is set if it had already expired when we first called wait
Edit:
Just checked and if the event is set when `wait` is called it doesn’t yield to the event loop and so as your examples above are written you really should’ve be able to get a `TimeoutError` unless you’re calling `set()` on the event from another thread which is a bug (use call_soon_threadsafe instead)