agronholm / anyio

High level asynchronous concurrency and networking framework that works on top of either trio or asyncio
MIT License
1.76k stars 134 forks source link

`fail_after` deadline is set on initialization not context entry #514

Open zanieb opened 1 year ago

zanieb commented 1 year ago

Discussed at https://gitter.im/python-trio/AnyIO?at=63ac6d617de82d261600ea24

When using a fail_after context, the deadline is set at "initialization" time rather than __enter__. See https://github.com/agronholm/anyio/blob/0cbb84bfadd9078c5dad63bab43907ed0dd555a1/src/anyio/_core/_tasks.py#L112-L114

import anyio

async def main():
    ctx = anyio.fail_after(5)
    await anyio.sleep(5)
    with ctx:
        for i in range(1, 6):
            print(i)
            await anyio.sleep(1)

anyio.run(main)
❯ python example.py
1
Traceback (most recent call last):
  File "/Users/mz/dev/prefect/example.py", line 168, in main
    await anyio.sleep(1)
  File "/opt/homebrew/Caskroom/miniconda/base/envs/orion-dev-39/lib/python3.9/site-packages/anyio/_core/_eventloop.py", line 83, in sleep
    return await get_asynclib().sleep(delay)
  File "/opt/homebrew/Caskroom/miniconda/base/envs/orion-dev-39/lib/python3.9/asyncio/tasks.py", line 652, in sleep
    return await future
asyncio.exceptions.CancelledError

Since this is a context manager, the user expectation is that the timer starts when the context is entered.

zanieb commented 1 year ago

This behavior matches trio, I've opened an issue for discussion there: https://github.com/python-trio/trio/issues/2512