I am trying to Yield From a function from within an asynchronous function. Having spent hours trying to figure this out and traversing Stack Overflow to find similar questions previously answered but unable to help me find a solution to my own problem, I find myself here.
Quite simply, I want to query the Asterisk Management Interface via Panoramisk, using a web browser and Websockets. When a user connects to the websocket server, it runs the ws_handle method
async def ws_handle(websocket, path):
await register(websocket)
try:
async for message in websocket:
data = json.loads(message)
...
I then want to retrieve some data, then deliver to the client. The problem which I am having, is that I find I am unable to just say
exts = yield from ExtensionStateList.get(AmiManager)
Where the ExtensionStateList.get function is (roughly) as below:
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
I have used this same file, ExtensionStateList.py in another test file separate from my websockets server file, in a non-async method, calling it as shown before
exts = yield from ExtensionStateList.get(AmiManager)
without a problem, and it populates exts with the value returned from the function.
My research leads me to iterate through it like so:
async for a in ExtensionStateList.get(AmiManager):
yield a
but I do not know how I can use that to populate the variable I wish to populate. I have tried like this:
exts = ''
async for a in ExtensionStatList.get(AmiManager):
exts = exts+a
only to be told that it cannot join an AsyncIO.Future to a string. I have also tried swapping out the return val
for a yield val
, again with no luck.
Evidently, to me, this is a shortcoming in my lacking knowledge of Python. What can I do? I was thinking that maybe I could change ExtensionStateList.get to async, but that would throw me back in the same boat I am in now?
ADDITIONALLY
I have continued scouring through StackOverflow, and found the following question:
What is the difference between @types.coroutine and @asyncio.coroutine decorators?
It seems to me that maybe if I add @asyncio.coroutine
on the line above ws_handle
, like so:
@asyncio.coroutine
async def ws_handle(websocket, path):
that I would then be able to:
exts = yield from ExtensionStateList.get(AmiManager)
However, I find that this does not work, and it tells me that I can not yield from inside an async function. Am I misunderstanding what I am reading here? Or am I maybe not implementing it correctly? Am I on the right track with this?
As per the answer given here:
'yield from' inside async function Python 3.6.5 aiohttp
I have also tried awaiting the function like so:
exts = await ExtensionStateList.get(AmiManager)
However, Python tells me that the object generator cannot be used in await expression.
FURTHERMORE
For those who may be interested, this is how I am calling my ws_handle function. It is called upon creation of the websocket server, and the websocket server is responsible for dispatching/calling? the ws_handle function.
It seems to me that it calls this function once for each client who connects and this function runs until the user disconnects.
WebsocketServer = websockets.serve(ws_handle, host, port)
asyncio.get_event_loop().run_until_complete(WebsocketServer)
asyncio.get_event_loop().run_forever()
ADDENDUM
Yes, again I add even more. I have modified my ExtensionStateList.py so that when calling the get method, it performs as per below:
async def get(AmiManager):
val = await getInternal(AmiManager)
return val
@asyncio.coroutine
def getInternal(AmiManager):
I can now use the yield from
internally in the getInternal function, which was previously my get function, and I can call this and receive date as per below:
exts = await ExtensionStateList.get(AmiManager)
I think I am getting a grasp of this, and I see how they are two different ways of doing almost the same thing.
Thanks for pointing me in the right direction guys!
You are confused a little by the change in asyncio syntax. In 3.5 we moved to async def and await. See this answer for details and note the example below:
You want an async generator. See Pep 525 and the example code:
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)