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