On 29 December 2015 at 21:53, Andrew Svetlov <[email protected]>
wrote:

> 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?
>

No, I missed something.  I missed the part where you catch CancelledError
and re-reaise as TimeoutError instead.

Looks good, then!

Cheers.


> Cheers.
>>
>> On 28 December 2015 at 10:40, Gustavo Carneiro <[email protected]>
>> wrote:
>>
>>>
>>>
>>> On 28 December 2015 at 06:27, Andrew Svetlov <[email protected]>
>>> 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
>>
>


-- 
Gustavo J. A. M. Carneiro
Gambit Research
"The universe is always one step beyond logic." -- Frank Herbert

Reply via email to