Python asyncio:满足`isinstance((基于发电机的程序),???)== True`吗?

nod*_*kai 4 python generator async-await python-asyncio

我百思不得其解既不的发现typing.Awaitable,也没有collections.abc.Awaitable涵盖发电的协程是一个awaitable定义小号

从Python 3.6开始,一些asyncioAPI(例如sleep()open_connection()实际上返回基于生成器的协程)。我通常有应用没有问题await关键字来的返回值,但我要处理正常的价值观和的混合物awaitable S和我需要找出哪些需要await产生的实际值。

所以这是我的问题,isinstance(c, ???) == True对于基于任意生成器的协程,满足什么条件c?我不是坚持isinstance要为此目的而使用,也许getattr()可以作为解决方案...

背景

我正在开发一个基于https://blog.miguelgrinberg.com/post/unit-testing-asyncio-code的微型函数实用程序,用于异步功能的单元测试,该函数内部具有一个模拟的asyncio.Queue()返回值,并且我想增强我的实用程序,以便队列可以包含等待元素,每个元素都会触发await操作。我的代码看起来像

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() # => '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
Run Code Online (Sandbox Code Playgroud)

出于可用性的原因,我不想使用手动包装返回值create_task()

我不确定我的队列是否会合法地包含普通的非协程生成器;仍然,理想的解决方案应该能够将常规生成器与基于生成器的协程区分开,并跳过await对前者的应用操作。

aba*_*ert 5

我不确定您要在此处测试什么,但是inspect模块具有用于检查大多数事情的功能,例如:

>>> async def f(c):
...     await c
>>> co = f()
>>> inspect.iscoroutinefunction(f)
True
>>> inspect.iscoroutine(co)
True
>>> inspect.isawaitable(co)
True
Run Code Online (Sandbox Code Playgroud)

后两者的区别在于,isawaitableawait不仅可以协程,还可以使用任何东西。

如果您真的想测试isinstance

isinstance(f)只是types.FunctionType,并不是很有用。要检查它是否是一个返回a的函数coroutine,您还需要检查其标志:(f.__code__.co_flags & inspect.CO_COROUTINE或者,如果inspect出于某种原因不想使用它,则可以硬编码0x80 )。

isinstance(co)types.CoroutineType,您可以测试一下,但这可能不是一个好主意。


use*_*342 4

记录的检测可传递对象的方法await是使用inspect.isawaitable.

根据PEP 492await需要一个可等待的对象,它可以是:

  • 原生协程-使用async def;定义的 Python 函数
  • @types.coroutine基于生成器的协程 - 用;修饰的 Python 生成器
  • __await__定义;的 Python 类的实例
  • 实现 的扩展类型的实例tp_as_async.am_await

isinstance(o, collections.abc.Awaitable)涵盖除第二个之外的所有内容。Awaitable如果没有明确记录,这可能会被报告为错误,指向inspect.isawaitable检查所有可等待的对象。

请注意,您无法通过检查类型来区分基于生成器的协程对象与常规生成器迭代器。两者具有完全相同的类型,因为coroutine装饰器不会包装给定的生成器,它只是在其代码对象上设置一个标志。检查该对象是否是由基于生成器的协程生成的生成器迭代器的唯一方法是检查其代码标志,这是如何inspect.isawaitable实现

一个相关的问题是为什么Awaitable只检查本身使用的其他机制是否存在__await__而不检查await。对于试图Awaitable检查对象的实际可等待性的代码来说,这是不幸的,但这并非没有先例。可迭代性和 ABC 之间也存在类似的差异Iterable

class Foo:
  def __getitem__(self, item):
      raise IndexError

>>> iter(Foo())
<iterator object at 0x7f2af4ad38d0>
>>> list(Foo())
[]
Run Code Online (Sandbox Code Playgroud)

尽管实例是Foo可迭代的,isinstance(Foo(), collections.abc.Iterable)但返回 false。