无法从异步函数内部产生并获取产生的数据

Unable to Yield From inside async function and get yielded data

我正在尝试从异步函数中的函数中产生。花了几个小时试图解决这个问题并遍历 Stack Overflow 以找到以前回答过的类似问题,但无法帮助我找到解决我自己问题的方法,我发现自己在这里。

很简单,我想使用网络浏览器和 Websockets 通过 Panoramisk 查询 Asterisk 管理界面。当用户连接到 websocket 服务器时,它运行 ws_handle 方法

async def ws_handle(websocket, path):
    await register(websocket)
    try:
        async for message in websocket:
            data = json.loads(message)
            ...

然后我想检索一些数据,然后传送给客户端。我遇到的问题是,我发现我不能只说

exts = yield from ExtensionStateList.get(AmiManager)

ExtensionStateList.get 函数(大致)如下所示:

def get(AmiManager):
    queues_details = yield from AmiManager.send_action(
        {'Action': 'ExtensionStateList'})

    ...

    val = {
        'extensions': EXTENSIONS,
        'parks': PARKS,
        'paging': PAGING,
        'confrences': CONFRENCES,
        'apps': APPS,
        'misc': MISC
    }

    return val

我在与我的 websockets 服务器文件分开的另一个测试文件中使用了同一个文件,ExtensionStateList.py,在非异步方法中,如前所示调用它

exts = yield from ExtensionStateList.get(AmiManager)

没问题,它用函数返回的值填充 exts。

我的研究让我像这样迭代它:

async for a in ExtensionStateList.get(AmiManager):
    yield a

但我不知道如何使用它来填充我希望填充的变量。我试过这样:

exts = ''
async for a in ExtensionStatList.get(AmiManager):
    exts = exts+a

只是被告知它不能将 AsyncIO.Future 连接到字符串。我也试过将 return val 换成 yield val,同样没有成功。

显然,对我来说,这是我对Python知识不足的一个缺点。我能做些什么?我在想也许我可以将 ExtensionStateList.get 更改为异步,但这会让我回到现在的同一条船上吗?

另外

我继续通过 Whosebug 进行搜索,发现了以下问题:

在我看来,也许如果我在 ws_handle 上面的行中添加 @asyncio.coroutine,就像这样:

@asyncio.coroutine
async def ws_handle(websocket, path):

然后我将能够:

exts = yield from ExtensionStateList.get(AmiManager)

但是,我发现这不起作用,它告诉我不能从异步函数内部产生。我误解了我在这里阅读的内容吗?或者我可能没有正确实施它?我这样做是否正确?

根据此处给出的答案:

我也试过像这样等待函数:

exts = await ExtensionStateList.get(AmiManager)

但是,Python告诉我对象生成器不能用于 await 表达式。

进一步

对于那些可能感兴趣的人,这就是我调用 ws_handle 函数的方式。它在创建 websocket 服务器时调用,websocket 服务器负责 dispatching/calling? ws_handle 函数。

在我看来,它会为每个连接的客户端调用一次该函数,并且该函数一直运行到用户断开连接为止。

WebsocketServer = websockets.serve(ws_handle, host, port)
asyncio.get_event_loop().run_until_complete(WebsocketServer)
asyncio.get_event_loop().run_forever()

附录

是的,我再次添加更多。我修改了我的 ExtensionStateList.py 以便在调用 get 方法时,它按以下方式执行:

async def get(AmiManager):
    val = await getInternal(AmiManager)
    return val

@asyncio.coroutine
def getInternal(AmiManager):

我现在可以在 getInternal 函数内部使用 yield from,它以前是我的 get 函数,我可以调用它并按照以下方式接收日期:

exts = await ExtensionStateList.get(AmiManager)

我想我已经掌握了这一点,我看到了它们是如何用两种不同的方式来做几乎相同的事情。

谢谢你们给我指明正确的方向!

保持想法-我没有弄清楚。根据我在下面的评论,我一定是累了,并且误以为程序运行出错了。哎呀

尤里卡!

经过几个小时的扯头发,阅读文档,几乎泪流满面,我发现 @async.coroutine 需要像这样应用于 ExtensionStateList.get 方法:

#In ExtensionStateList.py
@asyncio.coroutine
def get(AmiManager):

并且我必须像这样调用该命令时等待:

exts = ExtensionStateList.get(AmiManager)
await exts

我发现如果我只 await exts 而没有将其设为 asyncio.coroutine,它将 return 错误提示您不能等待生成器。

您对 asyncio 语法的变化感到有些困惑。在 3.5 中,我们转向了 async def 和 await。 并注意以下示例:

你想要一个异步生成器。请参阅 Pep 525 和示例代码:

async def ticker(delay, to):
    """Yield numbers from 0 to `to` every `delay` seconds."""
    for i in range(to):
        yield i
        await asyncio.sleep(delay)