Andrew Svetlov <andrew.svet...@gmail.com> added the comment:

In docs we can explain the behavior as "the outer expired timeout cancels the 
inner waiter, waits for CancelError bubbling up, and raising TimeoutError 
instead".

I agree that a counter is required for this behavior.
An alternative implementation can use the global `dict[Task, int]` for keeping 
counters. It can be either WeakKeyDictionary or a regular dict that deletes 
entries on `task.add_done_callback()` call.  We have a similar structure for 
`asyncio.all_tasks()` support already.

The global dict has a benefit: it doesn't overlap with the user's `.cancel()` 
calls but counts timeouts only.

A few words regarding task internals: _must_cancel boolean flag is set when a 
task doesn't wait for something, it was just created or `await sleep(0)` 
context switch was executed on the previous step.
Otherwise, a task always waits for a future completion, the future is stored as 
_fut_waiter.

If we use the global counting dict, timeout could call `.cancel()` only if the 
cancellation was not initiated previously. The current behavior works fine with 
this as the second `.cancel()` call is ignored.  Technically the ignorance 
could be reverted, `task.cancelling()` check is enough.

> If we don't set must-cancel, its cleanup is "shielded"

If I understand it correctly, I want this feature. Cleanup can perform async 
operations for a graceful resources shutdown, cancelling these cleaups look 
dangerous. With the current asyncio state, you can do it by calling 
`task.uncancel(); task.cancel()` in a line though.

----------

_______________________________________
Python tracker <rep...@bugs.python.org>
<https://bugs.python.org/issue46771>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to