title: python asyncio - how to wait for a cancelled shielded task?
body: <p>If I have a coroutine which runs a task which should not be cancelled, I will wrap that task in <code>asyncio.shield()</code>.</p>
  
  <p>It seems the behavior of <code>cancel</code> and <code>shield</code> is not what I would expect. If I have a task wrapped in <code>shield</code> and I cancel it, the <code>await</code>-ing coroutine returns from that <code>await</code> statement immediately rather than awaiting for the task to finish as <code>shield</code> would suggest. Additionally, the task that was run with <code>shield</code> continues to run but its future is now cancelled an not <code>await</code>-able.</p>
  
  <p>From the <a href="https://docs.python.org/3/library/asyncio-task.html#asyncio.shield" rel="noreferrer">docs</a>:</p>
  
  <blockquote>
    <p>except that if the coroutine containing it is cancelled, the Task running in something() is not cancelled. From the point of view of something(), the cancellation did not happen. Although its caller is still cancelled, so the “await” expression still raises a CancelledError.</p>
  </blockquote>
  
  <p>These docs do not imply strongly that the caller is cancelled potentially <em>before</em> the callee finishes, which is the heart of my issue.</p>
  
  <p>What is the proper method to <code>shield</code> a task from cancellation and then wait for it to complete before returning.</p>
  
  <p>It would make more sense if <code>asyncio.shield()</code> raised the <code>asyncio.CancelledError</code> after the <code>await</code>-ed task has completed, but obviously there is some other idea going on here that I don't understand.</p>
  
  <p>Here is a simple example:</p>
  
  <pre><code>import asyncio
  
  async def count(n):
    for i in range(n):
      print(i)
      await asyncio.sleep(1)
  
  async def t():
    try:
      await asyncio.shield(count(5))
    except asyncio.CancelledError:
      print('This gets called at 3, not 5')
  
    return 42
  
  async def c(ft):
    await asyncio.sleep(3)
  
    ft.cancel()
  
  async def m():
    ft = asyncio.ensure_future(t())
    ct = asyncio.ensure_future(c(ft))
  
    r = await ft
  
    print(r)
  
  loop = asyncio.get_event_loop()
  loop.run_until_complete(m())
  
  # Running loop forever continues to run shielded task
  # but I'd rather not do that
  #loop.run_forever()
  </code></pre>