Невозможно получить изнутри асинхронную функцию и получить данные
Я пытаюсь получить из функции внутри асинхронной функции. Потратив часы на то, чтобы выяснить это и обойти переполнение стека, чтобы найти похожие вопросы, на которые уже были даны ответы, но которые не помогли мне найти решение моей собственной проблемы, я оказался здесь.
Проще говоря, я хочу запросить интерфейс управления 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, он вернет ошибку, сообщающую, что вы не можете ждать генератора.