title: Python asyncio: what satisfies `isinstance( (generator-based coroutune), ???) == True`?
body: <p>I'm baffled to find out neither of <code>typing.Awaitable</code> nor <code>collections.abc.Awaitable</code> cover a generator-based coroutine which is one of <em>awaitable</em>s defined in</p>
  
  <ul>
  <li><a href="https://www.python.org/dev/peps/pep-0492/#await-expression" rel="nofollow noreferrer">https://www.python.org/dev/peps/pep-0492/#await-expression</a></li>
  </ul>
  
  <p>As of Python 3.6, several <code>asyncio</code> APIs such as <code>sleep()</code> and <code>open_connection()</code> actually return generator-based coroutines. I usually have no problems with applying <code>await</code> keywords to the return values of them, but I'm going to handle a mixture of normal values and <em>awaitable</em>s and I'd need to figure out which ones require <code>await</code> to yield an actual value.</p>
  
  <p>So here's my question, what satisfies <code>isinstance(c, ???) == True</code> for an arbitrary generator-based coroutine <code>c</code>?  I'm not insisting on using <code>isinstance</code> for this purpose, maybe <code>getattr()</code> can be a solution...</p>
  
  <h2>Background</h2>
  
  <p>I'm working on a tiny mock utility for unit testing of async function based on <a href="https://blog.miguelgrinberg.com/post/unit-testing-asyncio-code" rel="nofollow noreferrer">https://blog.miguelgrinberg.com/post/unit-testing-asyncio-code</a> which internally has a <code>asyncio.Queue()</code> of mocked return values, and I want to enhance my utility so that the queue can have <em>awaitable</em> elements, each of which triggering <code>await</code> operation. My code will look like</p>
  
  <pre><code>async def case01(loop):
    f = AsyncMock()
    f.side_effect = loop, []
    await f()  # blocks forever on an empty queue
  
  async def case02(loop):
    f = AsyncMock()
    f.side_effect = loop, ['foo']
    await f() # =&gt; 'foo'
    await f() # blocks forever
  
  async def case03(loop):
    f = AsyncMock()
    f.side_effect = loop, [asyncio.sleep(1.0, 'bar', loop=loop)]
    await f() # yields 'bar' after 1.0 sec of delay
  </code></pre>
  
  <p>For usability reason, I don't want to manually wrap the return values with <code>create_task()</code>.</p>
  
  <p>I'm not sure my queue will ever legitimely contain normal, non-coroutine generators; still, the ideal solution should be able to distinguish normal generators from generator-based coroutines and skip applying <code>await</code> operation to the former.</p>