Python 关闭无限异步发电机 再现误差

Python 关闭无限异步发电机 再现误差,python,python-asyncio,Python,Python Asyncio,我试图在在线回复中重现错误。但是,它的实现(以及行为)与我的实际代码并不完全相同(我在REPL中对position\u stream()中的响应执行异步,而不是对count()中的position()执行) 关于我的实际实现的更多细节 我在某个地方定义了这样一个协同程序: async def position(self): request = telemetry_pb2.SubscribePositionRequest() position_stream = self._stub

我试图在在线回复中重现错误。但是,它的实现(以及行为)与我的实际代码并不完全相同(我在REPL中对position\u stream()中的响应执行
异步,而不是对count()中的position()执行

关于我的实际实现的更多细节 我在某个地方定义了这样一个协同程序:

async def position(self):
    request = telemetry_pb2.SubscribePositionRequest()
    position_stream = self._stub.SubscribePosition(request)

    try:
        async for response in position_stream:
            yield Position.translate_from_rpc(response)
    finally:
        position_stream.cancel()
其中位置_流是无限的(或者可能是非常持久的)。我从如下示例代码中使用它:

async def print_altitude():
    async for position in drone.telemetry.position():
        print(f"Altitude: {position.relative_altitude_m}")
并且
print\u althip()
在循环上运行,具有:

asyncio.ensure_future(print_altitude())
asyncio.get_event_loop().run_forever()
这很有效。现在,在某个时刻,我想关闭来自调用者的流。我想我可以运行
asyncio。确保将来(loop.shutdown\u asyncgens())
并等待我的
最终被调用

相反,我收到一条关于未恢复异常的警告:

Task exception was never retrieved
future: <Task finished coro=<print_altitude() done, defined at [...]
从未检索到任务异常
未来:首先,如果您
停止一个循环,那么您的任何协同程序都没有机会正常关闭。调用
close
基本上意味着不可逆转地破坏循环

如果您不关心这些正在运行的任务会发生什么,您可以简单地
取消它们,这也将停止异步生成器:

import asyncio
from contextlib import suppress


async def position_stream():
    while True:
        await asyncio.sleep(1)
        yield 0

async def print_position():
    async for position in position_stream():
        print(f'position: {position}')

async def cleanup_awaiter():
    await asyncio.sleep(3)
    print('cleanup!')

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    try:
        asyncio.ensure_future(print_position())
        asyncio.ensure_future(print_position())
        loop.run_until_complete(cleanup_awaiter())
        # get all running tasks:
        tasks = asyncio.gather(*asyncio.Task.all_tasks())
        # schedule throwing CancelledError into the them:
        tasks.cancel()
        # allow them to process the exception and be cancelled:
        with suppress(asyncio.CancelledError):
            loop.run_until_complete(tasks)
    finally:
        print('closing loop')
        loop.close()

在问题中包含一个最小的runnable可能有助于获得答案。我尝试了一个最小的runnable,但这并不完全相同,因为我的流对于
不是异步的,而对于
s是正常的(参见第6行和第14行)。我相应地编辑了我的问题,并找到了一个很好的在线回复:[这里](编辑:我试图在一个在线REPL to replicate()中复制,但在您的示例中调用了finally子句,因为我得到了“取消位置流”和“取消高度流”在stdout中,我遗漏了什么吗?在这个例子中,这是对的,但我仍然得到了这个
任务异常从未被检索到
运行时错误。这不是真正的代码,因为我在真正的代码中实际使用了
异步,但我没有找到一种方法来编写一个完美再现错误的小示例。我希望我的玩具exaMPE将显示我做错了什么(即,修复此运行时错误将修复我真正的问题)。
task.cancel()
很有趣!我明天会检查!