Behavior of Timeout context manager is equal to using wait_for() without 
need for separate inner coroutine.

Like wait_for() cancels inner coroutine by sending CancelledError to inner 
and raising TimeoutError to outer. 


On Monday, December 28, 2015 at 1:00:27 PM UTC+2, Gustavo Carneiro wrote:
>
> Sorry for this 3rd email in a row, I'll shut up now.
>
> To clarify, my ideal Timeout context manager would be thus.  
>

> async def task():   # let's call this task t1
>    with Timeout(123):
>        await asyncio.sleep(321)  # let's call this task t2
>
> Given the above, outer task t1 is waiting for task t2, when the timeout 
> occurs.  The context manager should:
>   1. when entering the block: take a reference for the current task (as it 
> does now)
>   2. when the timeout occurs: 
>      a) take a note that the timeout occurred (as it does now)
>      b) somehow, find out which future our task t1 is waiting for (t2) and 
> cancel it
>   3. when exiting the block, if the timeout has occurred:
>      a) do NOT cancel the task t1
>      b) raise TimeoutError
>
> This way, normally the outer task gets cancelled anyway simply because of 
> the exception being raised.  However, there is the possibility for the code 
> to catch the exception and allow the rest of the task to continue executing 
> as if nothing had happened.
>
>
I believe it works exactly as you proposed.
Task t1 is not cancelled but it's resumed with TimeoutError.
The following test explicitly states this 
(https://github.com/KeepSafe/aiohttp/blob/master/tests/test_timeout.py#L112-L130):

>      has_timeout = False

    @asyncio.coroutine
    def outer():
        nonlocal has_timeout
        try:
            with Timeout(0.001, loop=loop):
                yield from asyncio.sleep(1, loop=loop)
        except asyncio.TimeoutError:
            has_timeout = True

    task = ensure_future(outer(), loop=loop)
    yield from task
    assert has_timeout
    assert not task.cancelled()
    assert task.done()

It works because Timeout() catches CancelledError on __exit__ and raises 
TimeoutError instead.

Did I miss something?

Cheers.
>
> On 28 December 2015 at 10:40, Gustavo Carneiro <[email protected] 
> <javascript:>> wrote:
>
>>
>>
>> On 28 December 2015 at 06:27, Andrew Svetlov <[email protected] 
>> <javascript:>> wrote:
>>
>>> asyncio has wait_for coroutine for timeouts.
>>>
>>> It works like:
>>>
>>> async def coro():
>>>     # do long running task
>>>     await asyncio.sleep(10000)
>>>
>>> await asyncio.wait_for(coro(), 1.5)
>>>
>>> The approach requires splitting code into two functions: one for waiting 
>>> and other for performing the task.
>>>
>>> aiohttp has Timeout class (
>>> https://github.com/KeepSafe/aiohttp/blob/master/aiohttp/helpers.py#L451-L488
>>> ).
>>> The code from example may be rewritten as:
>>>
>>> with Timeout(1.5):
>>>     # do long running task
>>>     await asyncio.sleep(10000)
>>>
>>>
>> In my last email I forgot to comment on your original text:
>>  
>>
>>> It raises asyncio.TimeoutError and cancels inner code if timeout runs 
>>> out.
>>>
>>
>> This statement is wrong.  It doesn't cancel just the "inner code".  If by 
>> "inner code" you mean the code inside the context manager block, then no, 
>> it cancels that code and any code that may come after.
>>
>> -- 
>> Gustavo J. A. M. Carneiro
>> Gambit Research
>> "The universe is always one step beyond logic." -- Frank Herbert
>>
>
>
>
> -- 
> Gustavo J. A. M. Carneiro
> Gambit Research
> "The universe is always one step beyond logic." -- Frank Herbert
>

Reply via email to