After cancelling an asyncio.gather() task i would expect task.cancelled() to return True but instead it returns False.
Questions
- Is this expected?
- While
Future.cancel()setsself._state = _CANCELLEDthe overridden_GatheringFuture.cancel()does not setself._state. (task.cancelled()usestask._state). Is this by design?
Example
def test_gather_cancellation_cancels_children_but_not_itself():
# loop
loop = new_event_loop()
set_event_loop(loop)
# create tasks and schedule them in gather
task_child = ensure_future(sleep(1.0, result=1))
task_gather = gather(task_child, return_exceptions=False)
# assert nothing cancelled/done
assert task_child.cancelled() is False
assert task_gather.cancelled() is False
assert task_child.done() is False
assert task_gather.done() is False
# cancel and await finishing of children
assert task_gather.cancel() is True
try:
with pytest.raises(CancelledError):
loop.run_until_complete(task_gather)
finally:
set_event_loop(None)
loop.close()
# assert all cancelled
assert task_child.cancelled() is True
# SURPRISING ASSERTION HERE:
# surprisingly this is False because the the internal
# task._state variable is not set in overridden
# _GatheringFuture.cancel()
assert task_gather.cancelled() is False # this is surprising, expected True
# assert all done
assert task_child.done() is True
assert task_gather.done() is True
============================= test session starts =============================
platform win32 -- Python 3.8.3rc1, pytest-5.4.2, py-1.8.1, pluggy-0.13.1 --
collecting ... collected 1 item
tests/gather/test_gather.py::test_gather_cancellation_cancels_children_but_not_itself PASSED [100%]
Docs
The Python docs mention this:
If gather() is cancelled, all submitted awaitables (that have not completed yet) are also cancelled. If any Task or Future from the aws sequence is cancelled, it is treated as if it raised CancelledError – the gather() call is not cancelled in this case. This is to prevent the cancellation of one submitted Task/Future to cause other Tasks/Futures to be cancelled.
However, it seems to me that this would apply only when gathered tasks are cancelled from outside! When running cancel() explicitly on the gather() task (and therefore its children), then it seems surprising that task.cancelled() is False. Therefore i'm not sure if the case described in the docs applies here.