I'm trying to experiment with the new await syntax for coroutines in Python 3.5.
I had a simple example like this:
#! /usr/bin/env python
import asyncio
#asyncio.coroutine
def adder(*args, delay):
while True:
yield from asyncio.sleep(delay)
print(sum(args))
def main():
asyncio.Task(adder(1, 2, 3, delay=5))
asyncio.Task(adder(10, 20, delay=3))
loop = asyncio.get_event_loop()
loop.run_forever()
if __name__ == "__main__":
main()
I changed the yield from line to use the await keyword:
await asyncio.sleep(delay)
And I get SyntaxError:
File "./test.py", line 8
await asyncio.sleep(delay)
^
SyntaxError: invalid syntax
So I try await (asyncio.sleep(delay)) just to see what happens:
Task exception was never retrieved
future: <Task finished coro=<adder() done, defined at c:\python35\Lib\asyncio\coroutines.py:198> exception=NameError("name 'await' is not defined",)>
Traceback (most recent call last):
File "c:\python35\Lib\asyncio\tasks.py", line 239, in _step
result = coro.send(value)
File "c:\python35\Lib\asyncio\coroutines.py", line 200, in coro
res = func(*args, **kw)
File "./test.py", line 8, in adder
await (asyncio.sleep(delay))
NameError: name 'await' is not defined
Task exception was never retrieved
future: <Task finished coro=<adder() done, defined at c:\python35\Lib\asyncio\coroutines.py:198> exception=NameError("name 'await' is not defined",)>
Traceback (most recent call last):
File "c:\python35\Lib\asyncio\tasks.py", line 239, in _step
result = coro.send(value)
File "c:\python35\Lib\asyncio\coroutines.py", line 200, in coro
res = func(*args, **kw)
File "./test.py", line 8, in adder
await (asyncio.sleep(delay))
NameError: name 'await' is not defined
Am I using the keyword wrong? Why is await not defined? I got my await syntax from this post.
Just to cover all my bases:
$ /usr/bin/env python --version
Python 3.5.0
Edit:
I suppose adding the parens to the await line is trying to call a function await() - so that's why that doesn't work and gives me a NameError. But why doesn't the keyword get recognized in either example?
You must declare your function as async to use await. Replacing yield from is not sufficient.
#! /usr/bin/env python
import asyncio
#asyncio.coroutine
async def adder(*args, delay):
while True:
await asyncio.sleep(delay)
print(sum(args))
def main():
asyncio.Task(adder(1, 2, 3, delay=5))
asyncio.Task(adder(10, 20, delay=3))
loop = asyncio.get_event_loop()
loop.run_forever()
if __name__ == "__main__":
main()
You need to declare your function as async to make this work. The await keyword will only be enabled if you include an a function declared with async def in your file – see PEP 492 for details.
Per PEP 492, the await keyword is only recognized in functions defined with the new async def syntax, which removes the need for a __future__ import.
Related
I am trying to run the below aiohttp client sample code straight out of aiohttp docs but it is throwing an exception.
import aiohttp
import asyncio
async def main():
async with aiohttp.ClientSession() as session:
async with session.get('http://python.org') as response:
print("Status:", response.status)
print("Content-type:", response.headers['content-type'])
html = await response.text()
print("Body:", html[:15], "...")
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Exception
Traceback (most recent call last):
File "aiohttp-test.py", line 16, in <module>
loop.run_until_complete(main())
File "/Users/waseem/.pyenv/versions/3.8.4/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete
return future.result()
File "aiohttp-test.py", line 6, in main
async with aiohttp.ClientSession() as session:
TypeError: __init__() missing 1 required positional argument: 'connector'
Versions of the required libs installed
Does anyone know what could be happening here?
I have the following function.
async def f(url):
async with httpx.AsyncClient() as client:
async with client.stream('GET', url) as resp:
async for c in resp.aiter_bytes():
yield c
And I'm writing a test code to test if the function yields.
#pytest.mark.asyncio
async def test():
c = None
async for c in f(URL):
c = chunk
break # there will be no error if removing this line. However, I don't need to loop all the items
assert c
However, the code runs but with the following warnings/errors. What's the error?
lib\site-packages\httpx_client.py:2012: UserWarning: Unclosed <httpx.AsyncClient object at 0x000001AC1E8CF3A0>. See https://www.python-httpx.org/async/#opening-and-closing-clients for details.
Is the "Unclosed" warning caused by break?
Exception ignored in: <function _ProactorBasePipeTransport.__del__ at 0x000001AC1D884DC0>
Traceback (most recent call last):
File "C:\anaconda3\envs\s\lib\asyncio\proactor_events.py", line 116, in __del__
self.close()
File "C:\anaconda3\envs\s\lib\asyncio\proactor_events.py", line 108, in close
self._loop.call_soon(self._call_connection_lost, None)
File "C:\anaconda3\envs\s\lib\asyncio\base_events.py", line 719, in call_soon
self._check_closed()
File "C:\anaconda3\envs\s\lib\asyncio\base_events.py", line 508, in _check_closed
raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed
The error disappears if removing the break statement.
In the answer of Async generator is not an iterator?, the following code will get rid of the error. However, it needs to call a -private- "special" function __anext()__().
#pytest.mark.asyncio
async def test():
it = f(URL):
c = it.__anext__()
assert c
Here's a simplified scenario for the problem I'm having.
import asyncio
async def c():
print("yes")
def b():
asyncio.run(c())
async def a():
b()
asyncio.run(a())
I'm expecting the program to print "yes". However, I get this instead:
Traceback (most recent call last):
File [redacted], line 12, in <module>
asyncio.run(a())
File "/usr/lib64/python3.7/asyncio/runners.py", line 43, in run
return loop.run_until_complete(main)
File "/usr/lib64/python3.7/asyncio/base_events.py", line 579, in run_until_complete
return future.result()
File [redacted], line 10, in a
b()
File [redacted], line 7, in b
asyncio.run(c())
File "/usr/lib64/python3.7/asyncio/runners.py", line 34, in run
"asyncio.run() cannot be called from a running event loop")
RuntimeError: asyncio.run() cannot be called from a running event loop
sys:1: RuntimeWarning: coroutine 'c' was never awaited
What would you think would be the solution for this problem?
(Also, can this be done using purely asyncio?)
Don't call asyncio.run() twice in the same program. Instead, once the event loop is running, create a task and await it. A sync function can create a new task but can't await it; it can return it as an awaitable. The async calling function can then perform the await.
import asyncio
async def c():
print("yes")
def b():
return asyncio.create_task(c())
async def a():
task = b()
await task
asyncio.run(a())
>>>yes
I am trying setup a flag for my async loop. I tried using asyncio.Event but I get a RuntimeError
Here's my code:
import sys
import asyncio
import logging
root = logging.getLogger()
root.setLevel(logging.DEBUG)
ch = logging.StreamHandler(sys.stdout)
ch.setFormatter(
logging.Formatter('%(asctime)s - %(name)s - %(levelname)s %(message)s')
)
root.addHandler(ch)
global event
event = asyncio.Event()
class AsyncTest():
async def bg(self):
root.debug('Waiting for 10 secs')
await asyncio.sleep(10)
event.set()
root.debug('Setted Event')
async def waiter(self):
event.clear()
root.debug('Created task')
asyncio.create_task(self.bg())
await asyncio.sleep(0)
root.debug('waiting')
await event.wait()
root.debug('Finished Waiting.')
return
async def main():
obj = AsyncTest()
await obj.waiter()
asyncio.run(main())
The bg courotine does a periodic task. Therefore it has to be run in asyncio.create_task() .
The waiter coroutine is called at multiple points to wait until the event is set.
So while running this i get this error:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Users\kevin\AppData\Local\Programs\Python\Python37\lib\asyncio\runners.py", line 43, in run
return loop.run_until_complete(main)
File "C:\Users\kevin\AppData\Local\Programs\Python\Python37\lib\asyncio\base_events.py", line 587, in run_until_complete
return future.result()
File "C:\Python Programs\Python\KLibs\NeuralConnect\test\test.py", line 39, in main
await obj.waiter()
File "C:\Python Programs\Python\KLibs\NeuralConnect\test\test.py", line 32, in waiter
await event.wait()
File "C:\Users\kevin\AppData\Local\Programs\Python\Python37\lib\asyncio\locks.py", line 293, in wait
await fut
RuntimeError: Task <Task pending coro=<main() running at C:\Python Programs\Python\KLibs\NeuralConnect\test\test.py:39> cb=[_run_until_complete_cb() at C:\Users\kevin\AppData\Local\Programs\Python\Python37\lib\asyncio\base_events.py:157]> got Future <Future pending> attached to a different loop
can anyone point out what is wrong in my above implementation? Also how should I correct? Should abandon the asyncio.Event() part and then use a bool variable?
The problem using the bool flag variable is that I have to do:
cond = False
while not cond:
await asyncio.sleep(0.3)
I find this un-pythonic. Is there any better implementation of this, incase asyncio.Event isnt suitable?
Finally I recieved an answer to this problem from alex_normane who answered a question linked to this. It seems that the asyncio.Event object has to passed as parameter to the coroutine in create_task(). Plus the asyncio.Event object must be created within a coroutine as also stated by user4815162342.
The answer is here: https://stackoverflow.com/a/65353254/13574717
I am using a third party API Wrapper that supports async. I am having a difficult time figuring out how to use it as I am new to asyncio, and I'm not sure if the errors I'm getting are my usage (most likely) or the wrapper itself.
The API can be found here: https://clashroyale.readthedocs.io/en/latest/api.html#royaleapi
The goal of my code is to make three API requests at once (the .get_clan(*lClanGroup)), asynchronously instead of synchronously.
My Code:
import clashroyale
import asyncio
import aiohttp
# Define Tokens
unofficialAPIToken = "secretToken"
# Get Client Objects
session1 = aiohttp.ClientSession()
unofficialClient = clashroyale.royaleapi.Client(unofficialAPIToken, session=session1, is_async=True)
lClanGroups = ['PPCLCJG9', '2LRU2J', 'PGLQ0VQ', 'YU2RQG9', '2LVRQ29'],['PYP8UPJV', 'P9L0CYY0', 'Y2RGQPJ', '8P2GYJ8', '9VQJPL2L'],['RYPRGCJ', '809R8PG8', 'PJY9PP98', '2GCQLC', '2GL2QPPL']
async def x(lClanGroup):
print(*lClanGroup)
a = await unofficialClient.get_clan(*lClanGroup) # Iterates five tags for the API to request at once
return a
async def y(lClanGroups):
result = await asyncio.gather(x(lClanGroups[0]),x(lClanGroups[1]),x(lClanGroups[2]))
return result
async def close_sessions():
await session1.close()
asyncio.run(y(lClanGroups))
asyncio.run(close_sessions())
The error I'm getting is long and difficult to make sense of:
C:\Users\Adam\PycharmProjects\ClashRoyaleMeta\venv2\Scripts\python.exe C:/Users/Adam/PycharmProjects/ClashRoyaleMeta/testme2.py
PPCLCJG9 2LRU2J PGLQ0VQ YU2RQG9 2LVRQ29
PYP8UPJV P9L0CYY0 Y2RGQPJ 8P2GYJ8 9VQJPL2L
RYPRGCJ 809R8PG8 PJY9PP98 2GCQLC 2GL2QPPL
C:/Users/Adam/PycharmProjects/ClashRoyaleMeta/testme2.py:16: DeprecationWarning: The object should be created from async function
session1 = aiohttp.ClientSession()
Traceback (most recent call last):
File "C:/Users/Adam/PycharmProjects/ClashRoyaleMeta/testme2.py", line 33, in <module>
asyncio.run(y(lClanGroups))
File "C:\Program Files\Python38\lib\asyncio\runners.py", line 43, in run
return loop.run_until_complete(main)
File "C:\Program Files\Python38\lib\asyncio\base_events.py", line 612, in run_until_complete
return future.result()
File "C:/Users/Adam/PycharmProjects/ClashRoyaleMeta/testme2.py", line 27, in y
result = await asyncio.gather(x(lClanGroups[0]),x(lClanGroups[1]),x(lClanGroups[2]))
File "C:/Users/Adam/PycharmProjects/ClashRoyaleMeta/testme2.py", line 23, in x
a = await unofficialClient.get_clan(*lClanGroup)
File "C:\Users\Adam\PycharmProjects\ClashRoyaleMeta\venv2\lib\site-packages\clashroyale\royaleapi\client.py", line 203, in _aget_model
raise e
File "C:\Users\Adam\PycharmProjects\ClashRoyaleMeta\venv2\lib\site-packages\clashroyale\royaleapi\client.py", line 196, in _aget_model
data, cached, ts, resp = await self._request(url, **params)
File "C:\Users\Adam\PycharmProjects\ClashRoyaleMeta\venv2\lib\site-packages\clashroyale\royaleapi\client.py", line 150, in _arequest
async with self.session.get(url, timeout=timeout, headers=self.headers, params=params) as resp:
File "C:\Users\Adam\PycharmProjects\ClashRoyaleMeta\venv2\lib\site-packages\aiohttp\client.py", line 1012, in __aenter__
self._resp = await self._coro
File "C:\Users\Adam\PycharmProjects\ClashRoyaleMeta\venv2\lib\site-packages\aiohttp\client.py", line 426, in _request
with timer:
File "C:\Users\Adam\PycharmProjects\ClashRoyaleMeta\venv2\lib\site-packages\aiohttp\helpers.py", line 579, in __enter__
raise RuntimeError('Timeout context manager should be used '
RuntimeError: Timeout context manager should be used inside a task
Unclosed client session
client_session: <aiohttp.client.ClientSession object at 0x0000016C5AD50EB0>
Process finished with exit code 1
Thanks for your help!
Each asyncio.run() creates new event loop. Moreover, if you create objects like aiohttp.ClientSession() globally, they may be bind to default event loop.
Using multiple event loops in your program is dangerous and may lead to non-obvious problems. To avoid the situation:
Run asyncio.run() only once
Create all asyncio-related stuff inside main coroutine
I didn't work with royaleapi, but it seems API developers do things differently than you in their tests. Here's how they innit/close stuff and here's how they get clan.
Let's give it a try?
import clashroyale
import asyncio
TOKEN = "secretToken"
clan_groups = [
['PPCLCJG9', '2LRU2J', 'PGLQ0VQ', 'YU2RQG9', '2LVRQ29'],
['PYP8UPJV', 'P9L0CYY0', 'Y2RGQPJ', '8P2GYJ8', '9VQJPL2L'],
['RYPRGCJ', '809R8PG8', 'PJY9PP98', '2GCQLC', '2GL2QPPL']
]
async def get_clans(cr, clan_groups):
return await asyncio.gather(*[
cr.get_clan(tag)
for group in clan_groups
for tag in group
])
async def main():
cr = clashroyale.RoyaleAPI(
TOKEN,
is_async=True,
timeout=30
)
try:
results = await get_clans(cr, clan_groups)
print(results)
finally:
await cr.close()
await asyncio.sleep(2)
asyncio.run(main())
Didn't test it.