So I'm locked to a python 3.6.2 interpreter that follows my desktop application.
What I want is to call an async function from a synchronized method or function.
When calling the python function from the desktop application it has to be a normal function which can not be awaited.
From the desktop application I am able to send a list of urls, and what I want is to send back response from every url in an async matter.
here is my try I've marked the SyntaxError which I don't know how to bypass.
import fmeobjects import asyncio import aiohttp import async_timeout logger = fmeobjects.FMELogFile() timeout = 10 class FeatureProcessor(object): def __init__(self): pass def input(self, feature): urls_and_coords = zip(feature.getAttribute('_list{}._wms'),\ feature.getAttribute('_list{}._xmin'),\ feature.getAttribute('_list{}._ymin'),\ feature.getAttribute('_list{}._xmax'),\ feature.getAttribute('_list{}._ymax')) -> SyntaxError: newfeature = await main(urls_and_coords) self.pyoutput(newfeature) def close(self): pass async def main(urls): loop = asyncio.get_event_loop() async with aiohttp.ClientSession(loop=loop) as session: feature = loop.run_until_complete(fetch_all(session, urls, loop)) return feature async def fetch_all(session, urls, loop): results = await asyncio.gather(*[loop.create_task(fetch(session, url)) for url in urls]) return results async def fetch(session, url): with async_timeout.timeout(10): async with session.get(url[0]) as response: newFeature = fmeobjects.FMEFeature() response_data = await response newFeature.setAttribute('response', response_data) newFeature.setAttribute('_xmin',url[1]) newFeature.setAttribute('_xmax',url[2]) newFeature.setAttribute('_ymin',url[3]) newFeature.setAttribute('_ymax',url[4]) return newFeature
I have tried making these changes:
import fme import fmeobjects import asyncio import aiohttp import async_timeout logger = fmeobjects.FMELogFile() class FeatureProcessor(object): def __init__(self): pass def input(self, feature): urls_and_coords = zip(feature.getAttribute('_list{}._wms'),\ feature.getAttribute('_list{}._xmin'),\ feature.getAttribute('_list{}._ymin'),\ feature.getAttribute('_list{}._xmax'),\ feature.getAttribute('_list{}._ymax')) loop = asyncio.get_event_loop() result = loop.run_until_complete(main(loop, urls_and_coords)) #feature.setAttribute('result',result) self.pyoutput(feature) def close(self): pass async def main(loop, urls): async with aiohttp.ClientSession(loop=loop) as session: return await fetch_all(session, urls, loop) async def fetch_all(session, urls, loop): results = await asyncio.gather(*[loop.create_task(fetch(session, url)) for url in urls]) return results async def fetch(session, url): with async_timeout.timeout(10): async with session.get(url[0]) as response: #newFeature = fmeobjects.FMEFeature() response = await response #newFeature.setAttribute('response', response_data) #newFeature.setAttribute('_xmin',url[1]) #newFeature.setAttribute('_xmax',url[2]) #newFeature.setAttribute('_ymin',url[3]) #newFeature.setAttribute('_ymax',url[4]) return response, url[1], url[2], url[3], url[4]
but now I end up with this error:
Python Exception <TypeError>: object ClientResponse can't be used in 'await' expression Traceback (most recent call last): File "<string>", line 20, in input File "asyncio\base_events.py", line 467, in run_until_complete File "<string>", line 29, in main File "<string>", line 33, in fetch_all File "<string>", line 41, in fetch TypeError: object ClientResponse can't be used in 'await' expression
To run an async function (coroutine) you have to call it using an Event Loop. Event Loops: You can think of Event Loop as functions to run asynchronous tasks and callbacks, perform network IO operations, and run subprocesses. Example 1: Event Loop example to run async Function to run a single async function: Python3.
Solution A If you have a simple asynchronous method that doesn't need to synchronize back to its context, then you can use Task. WaitAndUnwrapException : var task = MyAsyncMethod(); var result = task. WaitAndUnwrapException();
Conversely you absolutely can call non-async code from async-code, in fact it's easy to do so. But if a method/function call might “block” (ie. take a long time before it returns) then you really shouldn't. As you can see the counter has paused during the whole time it takes to make the HTTP request.
Inside an async function, you can use the await keyword before a call to a function that returns a promise. This makes the code wait at that point until the promise is settled, at which point the fulfilled value of the promise is treated as a return value, or the rejected value is thrown.
@deceze answer is probably the best you can do in Python 3.6. But in Python 3.7, you could directly use asyncio.run
in the following way:
newfeature = asyncio.run(main(urls))
It will properly create, handle, and close an event_loop
.
You would use an event loop to execute the asynchronous function to completion:
newfeature = asyncio.get_event_loop().run_until_complete(main(urls_and_coords))
(This technique is already used inside main
. And I'm not sure why, since main
is async
you could/should use await fetch_all(...)
there.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With