Message 413591 - Python tracker (original) (raw)
Suppose we have a case when two nested timeouts are reached at the same event loop iteration:
async def asyncio.timeout(1) as cm1: async with third_party_cm() as cm2: async def asyncio.timeout(1) as cm3: async with third_party_cm() as cm4: await asyncio.sleep(10)
What exception should be bubbled between outer and inner context manager 'exit' executions?
sleep()
is interrupted with CancelledError, it is clear (and the only possible solution in asyncio world). cm4.__aexit__
receives the CancelledError, does the cleanup if required, and re-raises the cancellation.
cm3.__aexit__
receives the bubbled CancelledError and updates its own state and raises an exception.
The question is: what exception should be raised, CancelledError or TimeoutError?
What exception should see cm2.__aexit__
code?
After careful thinking, I believe that CancelledError should be re-raised by inner affected timeout context managers, the only top-level affected context should convert CancelledError and raise TimeoutError.
My reasons for this behavior are:
A generic asyncio code is usually ready for cancellation. If it wants to react to the cancellation event, it caught asyncio.CancelledError
and reraised it. Also, the asyncio code is cancellation-ready by default because usually BaseException
is now handled (asyncio.CancelledError is derived from BaseException). TimeoutError is caught by except Exception
instead, it adds extra difficulty.
Handling both CancelledError and TimeoutError by any asyncio code on async stack unwinding is tedious and error-prone. If we should choose one I bet on CancelledError.
The inner code ignores timeouts usually (and executes resource cleanup only). That's what CancelledError handling exists for already. If the cleanup differs depending on timeout expiration, cm3.expired
(name it) can be used as a flag. You can disagree with me here, my opinion is based on my experience of writing asyncio code only.
The top-level affected timeout context manager should raise TimeoutError because it exists and is used for such things.
Long story short: all internal affected timeout context managers should not raise TimeoutError (or it should be configurable and 'off' by default) because third_party_cm()
should have the same simple implementation whether is it used as cm2
or cm4
.
Happy to see your opinions regarding the question, folks!