asyncio.gather cancellation behaviour

Stephan Houben stephanh42 at gmail.com.invalid
Fri Sep 8 11:10:15 EDT 2017


Hi all,

I am a bit mystified about the rationale of the cancellation
behaviour of asyncio.gather. 

  Case 1:
  "If the outer Future is cancelled, all children (that have not completed
  yet) are also cancelled."

  Case 2:
  "If any child is cancelled, this is treated as if it raised
  CancelledError – the outer Future is not cancelled in this case. 
  (THIS IS TO PREVENT THE CANCELLATION OF ONE CHILD TO CAUSE OTHER CHILDREN TO
  BE CANCELLED.)" [capitalization mine]

Indeed I can observe this behavior. However, I would like to further
understand the reasoning for not cancelling in case 2.

Outside asyncio.gather, cancelling an "outer future" does NOT cancel
the "inner future" on which the outer future is currently await-ing, while
cancelling the "inner future" will raise a CancelledError which will
cancel the outer future.

Example:
   
    import asyncio

    f1 = asyncio.Future()

    async def make_f2():
        await f1

    f2 = asyncio.ensure_future(make_f2())

    f2.cancel() # cancelling f1 instead will cancel BOTH f1 and f2

    loop = asyncio.get_event_loop()
    try:
        loop.run_until_complete(f2)
    except asyncio.CancelledError:
        print("got CancelledError")

    print("f1 cancelled: ", f1.cancelled()) # prints False
    print("f2 cancelled: ", f2.cancelled()) # prints True

So cancellation "normally" proceeds from inner future -> outer future.

It seems somebody worked hard to reverse the direction in case of
asyncio.gather. Why?

Stephan



More information about the Python-list mailing list