Description
Feature or enhancement
I propose that it becomes possible to raise arbitrary expressions on Task
objects, as well as provide custom instances
of asyncio.CancelledError
to `Task.cancel()
Pitch
Currently the only way to "signal" a Task
is by means of the Task.cancel()
method. It will cause a CancelledError
to be raised in the task in due course, and it can accept a special msg:str
argument.
However, the primary usage of this mechanism is to actually cancel tasks. This has been cemented into place with
the addition of the TaskGroup
construct, and various code put in place to ensure that Task.cancel()
results in task
cancellation. This includes the "cancel count" on each task.
Sending "signals" (Exceptions) to task objects shouldn't be considered a very exotic operation. The new asyncio.Timeout
context manager does that, when a timeout expires. However, it needs to co-opt the CancelledError
to do so. This becomes problematic because it needs
to distinguish between proper cancellations and timeouts. The implementation would be much cleaner if it could simply raise its own subclass of BaseException
, with its own custom arguments.
Task.cancel()
is not an async
method, meaning that cancellation is only scheduled to happen at a later point. (In a sense, it is asynchronous.)
Other frameworks, such as stackless have similar, but synchronous mechanisms to deliver signals. This would avoid race conditions where an application tries to send more than one exception to a task. Only one can be delivered, which one wins?
If we consider immediate exception delivery to hard to achieve in the asyncio
framework, we could instead decide on a
policy, such as "last exception wins", but wth "CancelledError" given special priority.
Here is a small list of suggested apis just to discuss this.
def cancel(msg:string="", exception:Optional[CancelledError]=None):
"""
Schedule a cancelled exception to be raised on the task. If `exception` is provided,
raise the given instance. `msg` is deprecated in favor or providing as an argument to the `exception` constructor.
"""
async def acancel(...):
"""
same as `cancel()` except it causes an immediate switch to the task.
"""
def raise(exception:BaseException):
"""
Schedule `exception` to be raised on the task. `exception must not be a `CancelledError` or
a `RuntimeError` will be thrown.
"""
async def araise(..):
"""
same as `raise()` except that the exception is delivered immediately.
"""
Previous discussion
This was started in issue #102780
Metadata
Metadata
Assignees
Projects
Status