Issue35945
Created on 2019-02-08 21:16 by jnwatson, last changed 2022-04-11 14:59 by admin. This issue is now closed.
| Messages (4) | |||
|---|---|---|---|
| msg335108 - (view) | Author: Nic Watson (jnwatson) | Date: 2019-02-08 21:16 | |
Goal: to distinguish inside a CancelledError exception handler whether the current running task is being cancelled, or another task that the current task is pending on was cancelled.
Example:
import asyncio
async def task2func():
await asyncio.sleep(2)
async def task1func(task2):
try:
await task2
except asyncio.CancelledError:
print("I don't know if I got cancelled")
async def main():
loop = asyncio.get_event_loop()
task2 = loop.create_task(task2func())
task1 = loop.create_task(task1func(task2))
await asyncio.sleep(0.5)
print('Cancelling first task')
task1.cancel()
await task1
await asyncio.sleep(0.5)
task2 = loop.create_task(task2func())
task1 = loop.create_task(task1func(task2))
await asyncio.sleep(0.5)
print('Cancelling second task')
task2.cancel()
await task1
asyncio.run(main())
If I have a task1 waiting on a task2, there's no public method (that I can tell) available in the task1func exception handler to distinguish between whether task1 or task2 were cancelled.
There is an internal field task_must_cancel in the C task struct that could be used to interrogate one's own current task to see if it is being cancelled. It is not available from Python (without ctypes hacking). The Python implementation's equivalent is called _must_cancel. (I'm not sure it is a good idea to export this from an API design perspective, but it does mean a mechanism exists.)
A workaround is that one can explicitly add attributes to the Python task object to communicate that a task is *starting to* be cancelled. This field would have the same purpose as the task_must_cancel field.
|
|||
| msg335829 - (view) | Author: twisteroid ambassador (twisteroid ambassador) * | Date: 2019-02-18 13:06 | |
There is a way to distinguish whether a task is being cancelled from the "inside" or "outside", like this:
async def task1func():
task2 = asyncio.create_task(task2func())
try:
await asyncio.wait((task2,))
except asyncio.CancelledError:
print('task1 is being cancelled from outside')
# Optionally cancel task2 here, since asyncio.wait() shields task2 from
# being cancelled from the outside
raise
assert task2.done()
if task2.cancelled():
print('task2 was cancelled')
# Note that task1 is not cancelled here, so if you want to cancel
# task1 as well, do this:
# raise asyncio.CancelledError
task2_result = task2.result()
# Use your result here
|
|||
| msg336022 - (view) | Author: Nic Watson (jnwatson) | Date: 2019-02-19 22:34 | |
Excellent answer by twisteroid Ambassador. Resolved |
|||
| msg336037 - (view) | Author: twisteroid ambassador (twisteroid ambassador) * | Date: 2019-02-20 03:17 | |
I wrote a recipe on this idea: https://gist.github.com/twisteroidambassador/f35c7b17d4493d492fe36ab3e5c92202 Untested, feel free to use it at your own risk. |
|||
| History | |||
|---|---|---|---|
| Date | User | Action | Args |
| 2022-04-11 14:59:11 | admin | set | github: 80126 |
| 2019-02-20 03:17:09 | twisteroid ambassador | set | messages: + msg336037 |
| 2019-02-19 22:34:59 | jnwatson | set | status: open -> closed resolution: not a bug messages: + msg336022 stage: resolved |
| 2019-02-18 13:06:28 | twisteroid ambassador | set | nosy:
+ twisteroid ambassador messages: + msg335829 |
| 2019-02-08 21:20:07 | epiphyte | set | nosy:
+ epiphyte |
| 2019-02-08 21:16:16 | jnwatson | create | |