Guido van Rossum <gu...@python.org> added the comment:

The 3rd party context managers are somewhat of a red herring.
These are just try/except or try/finally blocks.

The inner cm (cm4) is irrelevant, it will see CancelledError and presumably 
that bubbles out. If it does any resource cleanup we can replace it with 
try/finally for purposes of simplifying the example.

But here's an even simpler example that poses the same question:

async with asyncio.timeout(1) as cm1:
  try:
    async with asyncio.timeout(1) as cm3:
      await asyncio.sleep(10)  # Raises CancelledError
  except TimeoutError:
    print("timed out")

Does this see CancelledError or catch TimeoutError? I had naively expected that 
it would catch TimeoutError, but then there's no place for the outer cancel 
scope to have any effect, so I agree that it should indeed see CancelledError, 
and "timed out" is never printed. The outer cancel scope sees CancelledError 
and turns it into TimeoutError.

Note that if the outer cancel scope has a longer timeout (which isn't expired 
yet), the try/except will catch TimeoutError. If it then enters another `await 
asyncio.sleep(10)` it will be cancelled and the outer cancel scope will raise 
TimeoutError.

How to implement this behavior? It can be done with the "cancel counter" that I 
proposed and Tin implemented in https://github.com/python/cpython/pull/31434.

Can it be done with the simpler version (just a cancel-requested bit), without 
using a nonce? I don't think so -- we don't know in which order the cancel call 
from the inner and outer cancel scope happen, and if the inner goes first, it 
cannot be aware of the outer.

So I think the cancel counter is the minimal change needed.

I have one final question, to which I don't have a firm answer yet. In 
Task.cancel(), if the cancel counter is already nonzero, should it still go 
ahead and set the must-cancel flag (or pass the cancellation on to 
`self._fut_waiter` -- I am still not sure what that's for :-( ). I think it 
only makes a difference if the task being cancelled has already caught a 
CancelledError (from the first cancel()) and is handling it. If we set 
must-cancel, then if it uses `await` it will be cancelled again. If we don't 
set must-cancel, its cleanup is "shielded". **Opinions?**




(PS. There's a typo in Andrew's example -- it should be "async with", not 
"async def".)

----------

_______________________________________
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