Невозможно получить изнутри асинхронную функцию и получить данные

Я пытаюсь получить из функции внутри асинхронной функции. Потратив часы на то, чтобы выяснить это и обойти переполнение стека, чтобы найти похожие вопросы, на которые уже были даны ответы, но которые не помогли мне найти решение моей собственной проблемы, я оказался здесь.

Проще говоря, я хочу запросить интерфейс управления Asterisk через Panoramisk, используя веб-браузер и веб-сокеты. Когда пользователь подключается к серверу 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

Я использовал этот же файл ExtensionStateList.py в другом тестовом файле, отдельном от моего файла сервера websocket s, в неасинхронном методе, вызывая его, как показано ранее

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 на async, но это отбросило бы меня обратно в ту же лодку, в которой я сейчас нахожусь?

ДОПОЛНИТЕЛЬНО

Я продолжил просматривать Stack Overflow и нашел следующий вопрос:

В чем разница между декораторами @types.coroutine и @asyncio.coroutine?

Мне кажется, что, может быть, если я добавлю @asyncio.coroutine на линии выше ws_handle, вот так:

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

что я тогда смогу:

exts = yield from ExtensionStateList.get(AmiManager)

Тем не менее, я считаю, что это не работает, и это говорит мне, что я не могу дать внутри асинхронной функции. Я неправильно понимаю, что я читаю здесь? Или я, может быть, не правильно его реализую? Я на правильном пути с этим?

Согласно ответу, приведенному здесь:

'yield from' внутри асинхронной функции Python 3.6.5 aiohttp

Я также попытался дождаться этой функции:

exts = await ExtensionStateList.get(AmiManager)

Тем не менее, Python говорит мне, что генератор объектов нельзя использовать в выражении ожидания.

БОЛЕЕ ТОГО

Для тех, кто может быть заинтересован, вот как я вызываю свою функцию ws_handle. Он вызывается при создании сервера веб-сокетов, а сервер веб-сокетов отвечает за отправку / вызов? функция 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):

Теперь я могу использовать yield from внутренне в функции getInternal, которая ранее была моей функцией get, и я могу вызвать ее и получить дату, как показано ниже:

exts = await ExtensionStateList.get(AmiManager)

Я думаю, что я понимаю это, и я вижу, как это два разных способа сделать почти одно и то же.

Спасибо, что указали мне правильное направление, ребята!

2 ответа

Решение

Вас немного смущает изменение синтаксиса asyncio. В 3.5 мы перешли на async def и ждем. Посмотрите этот ответ для деталей и обратите внимание на пример ниже:

Вы хотите асинхронный генератор. Смотрите 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)

Задержи мысль - я не понял это. Согласно моему комментарию ниже, я, должно быть, устал, и принял ошибку за работу программы. К сожалению.

EUREKA!

После нескольких часов рвать волосы, читать документацию и почти слез, я обнаружил, что @async.coroutine должен быть применен к ExtensionStateList.get метод вроде так:

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

и что я должен ждать при вызове этой команды так:

exts = ExtensionStateList.get(AmiManager)
await exts

Я обнаружил, что если бы я только await exts не делая его asyncio.coroutine, он вернет ошибку, сообщающую, что вы не можете ждать генератора.

Другие вопросы по тегам