Return results with asyncio - python

I have the following code:
async def get_balance(exchange, symbol='BTC/USD'):
freeBalance = await exchange.fetchBalance()
symbol1_balance = freeBalance[symbol.split('/')[0]]
symbol2_balance = freeBalance[symbol.split('/')[1]]
return symbol1_balance, symbol2_balance
async def get_balances(exchanges):
futures = [get_balance(exchange) for exchange in exchanges]
balances = await asyncio.gather(*futures)
return balances
exchanges = [exchange1, exchange2, ...]
loop = asyncio.get_event_loop()
results = loop.run_until_complete(get_balances(exchanges))
But I get the error:
res = loop.run_until_complete(get_balances(exchanges, symbol))
Traceback (most recent call last):
File "C:\Users\Nicolas\Anaconda3\lib\asyncio\base_events.py", line
570, in run_until_complete
self.run_forever()
File "C:\Users\Nicolas\Anaconda3\lib\asyncio\base_events.py", line
525, in run_forever
raise RuntimeError('This event loop is already running')
RuntimeError: This event loop is already running
main:49: RuntimeWarning: coroutine 'binance.fetch_balance' was never awaited RuntimeWarning: Enable tracemalloc to get the object
allocation traceback
main:49: RuntimeWarning: coroutine 'bitfinex.fetch_balance' was never awaited RuntimeWarning: Enable tracemalloc to get the object
allocation traceback
To be clear:
print(exchanges[0].fetchBalance())
<coroutine object binance.fetch_ticker at 0x000001FE6473BA48>
So the function get_balance is already a coroutine.
I managed to run the first asyncio-example-gather except that instead of
asyncio.run(main())
File "C:\Users\Nicolas\Anaconda3\lib\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
But the following works:
loop = asyncio.get_running_loop()
res = loop.create_task(main())
I am pretty knew with asynchronous programming, was more using multithreading before. Do you know what's wrong here please?
Thank you guys!

Related

discord.py tasks.loop() sending runtime error : no running event loop

I am trying to make my bot send a message in a particular channel hourly using the discord.ext's tasks. Here's the buggy piece of code
#tasks.loop(hours = 1)
async def remindstudy():
await bot.wait_until_ready()
channel = bot.get_channel(1038719402658517054)
await channel.send("check")
remindstudy.start()
This is supposed to send the message "check" every hour but instead throws this error:
Traceback (most recent call last):
File "main.py", line 137, in <module>
remindstudy.start()
File "/home/runner/main-bore-ho-raha-hoon/venv/lib/python3.8/site-packages/discord/ext/tasks/__init__.py", line 398, in start
self._task = asyncio.create_task(self._loop(*args, **kwargs))
File "/nix/store/2vm88xw7513h9pyjyafw32cps51b0ia1-python3-3.8.12/lib/python3.8/asyncio/tasks.py", line 381, in create_task
loop = events.get_running_loop()
RuntimeError: no running event loop
sys:1: RuntimeWarning: coroutine 'Loop._loop' was never awaited
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
What is going wrong? I copied this code snippet from another old bot of mine, it is still running perfectly fine, but this bot won't run
The task.start method is expected to be called in an async function (coroutine), like in setup_hook, after the 2.0 update.
async def setup_hook():
remindstudy.start()
bot.setup_hook = setup_hook
#tasks.loop(hours = 1)
async def remindstudy():
await bot.wait_until_ready()
channel = bot.get_channel(1038719402658517054)
await channel.send("check")
See this example.

Call an async function periodically on a Tornado web server?

I have a web site using Tornado. And I need to call a function (do_work() in the following exmaple) periodically.
import asyncio
import datetime
from typing import Any, Callable, Coroutine
import tornado
import tornado.ioloop
def schedule_next_sync(loop, func, seconds):
time = datetime.datetime.timestamp(
datetime.datetime.now() + datetime.timedelta(seconds=seconds)
)
def wrapper():
loop.run_until_complete(func())
schedule_next_sync(loop, func, seconds)
print("The next run is sheduled for %s", datetime.datetime.fromtimestamp(time))
tornado.ioloop.IOLoop.current().add_timeout(time, wrapper)
async def do_work(x):
"""To be run periodically"""
print(f"...{datetime.datetime.now()}: {x}")
await asyncio.sleep(1)
print('....')
event_loop = asyncio.get_event_loop()
_func = lambda: do_work("test")
schedule_next_sync(event_loop, _func, 3) # every 3 seconds
tornado.ioloop.IOLoop.current().start()
However, the code got the following error?
The next run is sheduled for %s 2022-03-30 01:48:23.141079
ERROR:tornado.application:Exception in callback functools.partial(<function schedule_next_sync.<locals>.wrapper at 0x000001A7D6B99700>)
Traceback (most recent call last):
File "C:\users\xxx\anaconda3\envs\x\lib\site-packages\tornado\ioloop.py", line 741, in _run_callback
ret = callback()
File ".\test.py", line 23, in wrapper
loop.run_until_complete(func())
File "C:\users\xxx\anaconda3\envs\x\lib\asyncio\base_events.py", line 592, in run_until_complete
self._check_running()
File "C:\users\xxx\anaconda3\envs\x\lib\asyncio\base_events.py", line 552, in _check_running
raise RuntimeError('This event loop is already running')
RuntimeError: This event loop is already running
C:\users\xxx\anaconda3\envs\x\lib\site-packages\tornado\ioloop.py:761: RuntimeWarning: coroutine 'do_work' was never awaited
app_log.error("Exception in callback %r", callback, exc_info=True)
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
You cannot use loop.run_until_complete() when the event loop is already running.
To schedule the execution of async code from sync code or to run async code concurrently, you need to use asyncio.create_task().
Your example works for me, if I change the wrapper to this:
def wrapper():
asyncio.create_task(func())
schedule_next_sync(loop, func, seconds)

Python websocket in a thread

I try to run a websocket in python in a thread. Here is the example I made:
import asyncio
import websockets
import threading
class websocket(threading.Thread):
def run(self):
asyncio.get_event_loop().run_until_complete(
websockets.serve(self.echo, 'localhost', 8765))
asyncio.get_event_loop().run_forever()
async def echo(websocket, path):
async for message in websocket:
await websocket.send(message)
ws = websocket()
ws.start()
I get that error:
Exception in thread Thread-1:
Traceback (most recent call last):
File "/usr/lib/python3.6/threading.py", line 916, in _bootstrap_inner
self.run()
File "/home/xavier/dev/3003_mdt/mdt-robotarm/robotarm_server.py", line 7, in run
asyncio.get_event_loop().run_until_complete(
File "/usr/lib/python3.6/asyncio/events.py", line 694, in get_event_loop
return get_event_loop_policy().get_event_loop()
File "/usr/lib/python3.6/asyncio/events.py", line 602, in get_event_loop
% threading.current_thread().name)
RuntimeError: There is no current event loop in thread 'Thread-1'.
Process finished with exit code 0
How can I run the websocket in a thread?
You've got the error because get_event_loop creates the loop automatically only in the main thread. Create the loop manually with new_event_loop like this:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(
websockets.serve(self.echo, "localhost", 8765)
)
loop.close()

Awaiting an asyncio.Future raises concurrent.futures._base.CancelledError instead of waiting for a value/exception to be set

When I run the following python code:
import asyncio
import logging
logging.basicConfig(level=logging.DEBUG)
async def read_future(fut):
print(await fut)
async def write_future(fut):
fut.set_result('My Value')
async def main():
loop = asyncio.get_running_loop()
fut = loop.create_future()
asyncio.gather(read_future(fut), write_future(fut))
asyncio.run(main(), debug=True)
Instead of read_future waiting for the result of fut to be set, the program crashes with the following error:
DEBUG:asyncio:Using selector: KqueueSelector
ERROR:asyncio:_GatheringFuture exception was never retrieved
future: <_GatheringFuture finished exception=CancelledError() created at /Users/<user>/.pyenv/versions/3.7.4/lib/python3.7/asyncio/tasks.py:642>
source_traceback: Object created at (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/<user>/.pyenv/versions/3.7.4/lib/python3.7/asyncio/runners.py", line 43, in run
return loop.run_until_complete(main)
File "/Users/<user>/.pyenv/versions/3.7.4/lib/python3.7/asyncio/base_events.py", line 566, in run_until_complete
self.run_forever()
File "/Users/<user>/.pyenv/versions/3.7.4/lib/python3.7/asyncio/base_events.py", line 534, in run_forever
self._run_once()
File "/Users/<user>/.pyenv/versions/3.7.4/lib/python3.7/asyncio/base_events.py", line 1763, in _run_once
handle._run()
File "/Users/<user>/.pyenv/versions/3.7.4/lib/python3.7/asyncio/events.py", line 88, in _run
self._context.run(self._callback, *self._args)
File "<stdin>", line 4, in main
File "/Users/<user>/.pyenv/versions/3.7.4/lib/python3.7/asyncio/tasks.py", line 766, in gather
outer = _GatheringFuture(children, loop=loop)
File "/Users/<user>/.pyenv/versions/3.7.4/lib/python3.7/asyncio/tasks.py", line 642, in __init__
super().__init__(loop=loop)
concurrent.futures._base.CancelledError
DEBUG:asyncio:Close <_UnixSelectorEventLoop running=False closed=False debug=True>
What am I doing wrong in this code? I want to be able to await the Future fut and continue after the Future has a value/exception set.
Your problem is that asyncio.gather is itself async (returns an awaitable); by not awaiting it, you never handed control back to the event loop, nor did you store the awaitable, so it was immediately cleaned up, implicitly cancelling it, and by extension, all of the awaitables it controlled.
To fix, just make sure you await the results of the gather:
await asyncio.gather(read_future(fut), write_future(fut))
Try it online!
From https://docs.python.org/3/library/asyncio-future.html#asyncio.Future.result:
result()
Return the result of the Future.
If the Future is done and has a result set by the set_result() method, the result value is returned.
If the Future is done and has an exception set by the set_exception() method, this method raises the exception.
If the Future has been cancelled, this method raises a CancelledError exception.
If the Future’s result isn’t yet available, this method raises a InvalidStateError exception.
(Bold added.)
I'm not sure why the Future is getting cancelled, but that seems to be the cause of the issue.

Python asyncio websocket ConnectionClosed

i have multiple tasks that use websocket. i would like to detect the connection close of websocket and then reconnect the websocket, I'm doing it through a variable called break_task if it is true then in on of the task there was a connection close detection, below the code and the error exception I got (i use python 3.6):
break_task = False
async def value_1():
global break_task
msg_dict = deepcopy(MESG)
async with websockets.connect('wss://api.xxxxx') as ws:
await ws.send(json.dumps(msg_dict))
while break_task == False:
try:
res = await ws.recv()
except ConnectionClosed:
print('CONNECTION CLOSED raised exception in value_1() -> TRYING TO RECONNECT !!!')
break_task = True
break
async def value_2():
global break_task
auth_dict = deepcopy(PAYLOAD)
async with websockets.connect('wss://api.xxxxx') as ws:
await ws.send(json.dumps(auth_dict))
while break_task == False:
await asyncio.sleep(0.05)
try:
res = await ws.recv()
except ConnectionClosed:
print('CONNECTION CLOSED raised exception in value_2() -> TRYING TO RECONNECT !!!')
break_task = True
break
data = json.loads(res)
and...
while True:
if (break_task == False):
tasks = value_1(), value_2()
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.gather(*tasks))
elif (break_task == True):
tasks = None
loop.stop()
loop.close()
break_task = False
I get this result:
Traceback (most recent call last):
File "tija_v0.1.py", line 584, in <module>
loop.run_until_complete(asyncio.gather(*tasks))
File "C:\Dev\Anaconda2\envs\python36\lib\asyncio\tasks.py", line 589, in gather
fut = ensure_future(arg, loop=loop)
File "C:\Dev\Anaconda2\envs\python36\lib\asyncio\tasks.py", line 513, in ensure_future
task = loop.create_task(coro_or_future)
File "C:\Dev\Anaconda2\envs\python36\lib\asyncio\base_events.py", line 282, in create_task
self._check_closed()
File "C:\Dev\Anaconda2\envs\python36\lib\asyncio\base_events.py", line 357, in _check_closed
raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed
sys:1: RuntimeWarning: coroutine 'value_2' was never awaited
sys:1: RuntimeWarning: coroutine 'value_1' was never awaited
sys:1: RuntimeWarning: coroutine 'value_2' was never awaited
sys:1: RuntimeWarning: coroutine 'value_1' was never awaited
might trip your process from reconnecting, it might effect in more severe way than a warning. I have similar problem when attempting to bind async message update_handler function to synchronic third party web socket message handler.
You should find a method to call the handler with await to prevent this warning.

Categories

Resources