[Async-sig] subclassing CancelledError

Chris Jerdonek chris.jerdonek at gmail.com
Sun Dec 3 16:53:12 EST 2017


On Sun, Dec 3, 2017 at 9:04 AM, Guido van Rossum <guido at python.org> wrote:
> Sounds like an implementation issue. The Future class has a boolean flag
> indicating whether it's been cancelled, and everything then raises
> CancelledError when that flag is set. I suppose we could replace that flag
> with an instance of CancelledError, and when we *catch* CancelledError we
> set it to that. But it seems messy and I'm not sure that you should attempt
> this. IMO you should define an exception that does *not* derive from
> CancelledError and raise that -- it will properly be transmitted. What's
> your reason for not doing that? IOW why are you trying to pump more than a
> bit through the narrow "cancelled" channel?

My use case is mostly for diagnostic / logging purposes. I want to log
all exceptions bubbling out of a task, and I want to do so from within
the coroutine itself rather than when I call task.result(). I also
want to be able to detect if something wasn't logged when I call
task.result() (to avoid double logging and errors passing silently,
etc), and I'd also prefer that task.cancelled(), etc. still return the
correct values.

My first idea was to define LoggedError(Exception) and
LoggedCancelledError(CancelledError) subclasses, and raise a new
exception from within the task after logging. If LoggedCancelledError
doesn't derive from CancelledError, then task.cancelled() won't
reflect that the task was cancelled.

Could it simplify things on the asyncio side if the CancelledError and
non-CancelledError code paths shared more logic (e.g. by both setting
an exception instead of only the generic case setting an exception)?

--Chris


>
> On Sun, Dec 3, 2017 at 2:11 AM, Andrew Svetlov <andrew.svetlov at gmail.com>
> wrote:
>>
>> IIRC at very early stages Guido van Rossum decided to *freeze*
>> `CancelledError`: user code should not derive from the exception. Like you
>> never derive from StopIteration.
>>
>> On Sun, Dec 3, 2017 at 8:00 AM Chris Jerdonek <chris.jerdonek at gmail.com>
>> wrote:
>>>
>>> Hi, I want to ask how people feel about the following.
>>>
>>> If you raise a subclass of CancelledError from within a task and then
>>> call task.result(), CancelledError is raised rather than the subclass.
>>>
>>> Here is some code to illustrate:
>>>
>>>     class MyCancelledError(CancelledError): pass
>>>
>>>     async def raise_my_cancel():
>>>         raise MyCancelledError()
>>>
>>>     task = asyncio.ensure_future(raise_my_cancel())
>>>     try:
>>>         await task
>>>     except Exception:
>>>         pass
>>>     assert task.cancelled()
>>>     # Raises CancelledError and not MyCancelledError.
>>>     task.result()
>>>
>>> Does this seem right to people? Is there a justification for this?
>>>
>>> If it would help for the discussion, I could provide a use case.
>>>
>>> Thanks a lot,
>>> --Chris
>>> _______________________________________________
>>> Async-sig mailing list
>>> Async-sig at python.org
>>> https://mail.python.org/mailman/listinfo/async-sig
>>> Code of Conduct: https://www.python.org/psf/codeofconduct/
>>
>> --
>> Thanks,
>> Andrew Svetlov
>>
>> _______________________________________________
>> Async-sig mailing list
>> Async-sig at python.org
>> https://mail.python.org/mailman/listinfo/async-sig
>> Code of Conduct: https://www.python.org/psf/codeofconduct/
>>
>
>
>
> --
> --Guido van Rossum (python.org/~guido)


More information about the Async-sig mailing list