简体   繁体   中英

Unable to Yield From inside async function and get yielded data

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)

Hold the thought - I didn't figure it out. As per my comment below, I must have been tired, and mistook an error for the program working. Oops.

EUREKA!

After hours of hair tearing, documentation reading, and almost tears, I found that @async.coroutine needs to be applied to the ExtensionStateList.get method like so:

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

and that I must await when calling that command like so:

exts = ExtensionStateList.get(AmiManager)
await exts

I found that if I only await exts without making it an asyncio.coroutine, it will return the error stating that you cannot await a generator.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM