Asyncio coroutine never awaited error - python

I'm having trouble fixing and understanding the problem here. I'm using an example to learn Asyncio but the code I'm using is similar to mine but mine gives an error message saying:
sys:1: RuntimeWarning: coroutine 'run_script' was never awaited
Please any help will be greatly appreciated. Below is my code
async def run_script(script):
print("Run", script)
await asyncio.sleep(1)
os.system("python " + script)
and I'm running it like this
for script in os.listdir():
if script.endswith(".py"):
scripts.append(run_script(script))
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.gather(scripts))
loop.close()

As #dim mentioned what's the typo in your code, you also need to be aware os.system is running in synchronous, which means scripts in your folder will be run in sequence instead of in asynchronous way.
To understand that, add file called hello_world.py:
import time
time.sleep(2)
print('hello world')
if you run you script as follows, it will cost you 2s + 2s = 4s:
loop = asyncio.get_event_loop()
loop.run_until_complete(
asyncio.gather(
*[run_script('hello_world.py') for _ in range(2)]
)
)
So to solve this issue, you can use asyncio.subprocess module:
from asyncio import subprocess
async def run_script(script):
process = await subprocess.create_subprocess_exec('python', script)
try:
out, err = await process.communicate()
except Exception as err:
print(err)
Then it will cost you only 2 sec, because it is running asynchronously.

Related

How to gracefully end asyncio program with CTRL-C when using loop run_in_executor

The following code requires 3 presses of CTRL-C to end, how can I make it end with one only? (So it works nicely in Docker)
import asyncio
import time
def sleep_blocking():
print("Sleep blocking")
time.sleep(1000)
async def main():
loop = asyncio.get_event_loop()
await loop.run_in_executor(None, sleep_blocking)
try:
asyncio.run(main())
except KeyboardInterrupt:
print("Nicely shutting down ...")
I've read many asyncio related questions and answers but can't figure this one out yet. The 1st CTRL-C does nothing, the 2nd prints "Nicely shutting down ..." and then hangs. The 3rd CTRL-C prints an ugly error.
I'm on Python 3.9.10 and Linux.
(edit: updated code per comment #mkrieger1)
The way to exit immediately and unconditionally from a Python program is by calling os._exit(). If your background threads are in the middle of doing something important this may not be wise. However the following program does what you asked (python 3.10, Windows10):
import asyncio
import time
import os
def sleep_blocking():
print("Sleep blocking")
time.sleep(1000)
async def main():
loop = asyncio.get_event_loop()
loop.run_until_complete(loop.run_in_executor(None, sleep_blocking))
try:
asyncio.run(main())
except KeyboardInterrupt:
print("Nicely shutting down ...")
os._exit(42)
From here we know that it's effectively impossible to kill a task running in a thread executor. If I replace the default thread executor with a ProcessPoolExecutor, I get the behavior you're looking for. Here's the code:
import concurrent.futures
import asyncio
import time
def sleep_blocking():
print("Sleep blocking")
time.sleep(1000)
async def main():
loop = asyncio.get_event_loop()
x = concurrent.futures.ProcessPoolExecutor()
await loop.run_in_executor(x, sleep_blocking)
try:
asyncio.run(main())
except KeyboardInterrupt:
print("Nicely shutting down ...")
And the result is:
$ python asynctest.py
Sleep blocking
^CNicely shutting down ...

Python asyncio not executing

I'm not really a Python expert, so excuse me if this is really obvious. I'm trying to run a script using asyncio. Relevant bits of code:
import websockets
import asyncio
stream = websockets.connect(<resource_uri>)
async def main():
async with stream as receiver:
while True:
data = receiver.recv()
# do stuff
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
When I run this, I get:
DeprecationWarning: There is no current event loop
loop = asyncio.get_event_loop()
Similarly, using
'''
loop = asyncio.get_running_loop()
'''
instead, I get
RuntimeError: no running event loop.
Any ideas? I guess it's something to do with main() not running in the correct thread...
I'm using Python 3.10.
Newer code should use just asyncio.run(main()) - that will automatically create a new instance loop and "run until complete" on the awaitable.
Try this, works for me on 3.8 (and probably originally got from someone smarter than me that posted it on here!!)
try:
loop = asyncio.get_running_loop()
except RuntimeError: # 'RuntimeError: There is no current event loop...'
loop = None
if loop and loop.is_running():
print('Async event loop already running. Adding coroutine to the event loop.')
tsk = loop.create_task(main())
# ^-- https://docs.python.org/3/library/asyncio-task.html#task-object
# Optionally, a callback function can be executed when the coroutine completes
tsk.add_done_callback(
lambda t: print(f'Task done with result = {t.result()}'))
else:
print('Starting new event loop')
asyncio.run(main())
It seems there's an issue with websockets and Python 3.10. Looks like I'll have to find a workaround. Thanks for your time!

Async Errors in python

I'm coding a telegram userbot (with telethon) which sends a message,every 60 seconds, to some chats.
I'm using 2 threads but I get the following errors: "RuntimeWarning: coroutine 'sender' was never awaited" and "no running event loop".
My code:
....
async def sender():
for chat in chats :
try:
if chat.megagroup == True:
await client.send_message(chat, messaggio)
except:
await client.send_message(myID, 'error')
schedule.every(60).seconds.do(asyncio.create_task(sender()))
...
class checker1(Thread):
def run(self):
while True:
schedule.run_pending()
time.sleep(1)
class checker2(Thread):
def run(self):
while True:
client.add_event_handler(handler)
client.run_until_disconnected()
checker2().start()
checker1().start()
I searched for a solution but I didn't find anything...
You should avoid using threads with asyncio unless you know what you're doing. The code can be rewritten using asyncio as follows, since most of the time you don't actually need threads:
import asyncio
async def sender():
for chat in chats :
try:
if chat.megagroup == True:
await client.send_message(chat, messaggio)
except:
await client.send_message(myID, 'error')
async def checker1():
while True:
await sender()
await asyncio.sleep(60) # every 60s
async def main():
await asyncio.create_task(checker1()) # background task
await client.run_until_disconnected()
client.loop.run_until_complete(main())
This code is not perfect (you should properly cancel and wait checker1 at the end of the program), but it should work.
As a side note, you don't need client.run_until_disconnected(). The call simply blocks (runs) until the client is disconnected. If you can keep the program running differently, as long as asyncio runs, the client will work.
Another thing: bare except: are a very bad idea, and will probably cause issues with exception. At least, replace it with except Exception.
There are a few problems with your code. asyncio is complaining about "no running event loop" because your program never starts the event loop anywhere, and tasks can't be scheduled without an event loop running. See Asyncio in corroutine RuntimeError: no running event loop. In order to start the event loop, you can use asyncio.run_until_complete() if you have a main coroutine for your program, or you can use asyncio.get_event_loop().run_forever() to run the event loop forever.
The second problem is the incorrect usage of schedule.every(60).seconds.do(), which is hidden by the first error. schedule expects a function to be passed in, not an awaitable (which is what asyncio.create_task(sender()) returns). This normally would have caused a TypeError, but the create_task() without a running event loop raised an exception first, so this exception was never raised. You'll need to define a function and then pass it to schedule, like this:
def start_sender():
asyncio.create_task(sender())
schedule.every(60).seconds.do(start_sender)
This should work as long as the event loop is started somewhere else in your program.

python asyncio coroutine was never awaited

I am using python 3.8 with this code
async def main():
pass
async def build():
pass
asyncio.create_task(build())
loop = asyncio.get_event_loop()
asyncio.create_task(main())
pending = asyncio.all_tasks()
loop.run_until_complete(asyncio.gather(*pending))
and get the following error
sys:1: RuntimeWarning: coroutine 'build' was never awaited
What am I missing here? shouldn't run until complete wait for all the tasks to finish?
As the loop is not running when creating the task, asyncio is unable to attach tasks to it.
This can be fixed by replacing asyncio.create_task() with loop.create_task().
The full error can be retrieved using asyncio.gather(..., return_exceptions=True) so gather() will raise a RuntimeError: no running event loop.

How to gracefully terminate an asyncio script with Ctrl-C?

I've read every post I could find about how to gracefully handle a script with an asyncio event loop getting terminated with Ctrl-C, and I haven't been able to get any of them to work without printing one or more tracebacks as I do so. The answers are pretty much all over the place, and I haven't been able implement any of them into this small script:
import asyncio
import datetime
import functools
import signal
async def display_date(loop):
end_time = loop.time() + 5.0
while True:
print(datetime.datetime.now())
if (loop.time() + 1.0) >= end_time:
break
await asyncio.sleep(1)
def stopper(signame, loop):
print("Got %s, stopping..." % signame)
loop.stop()
loop = asyncio.get_event_loop()
for signame in ('SIGINT', 'SIGTERM'):
loop.add_signal_handler(getattr(signal, signame), functools.partial(stopper, signame, loop))
loop.run_until_complete(display_date(loop))
loop.close()
What I want to happen is for the script to exit without printing any tracebacks following a Ctrl-C (or SIGTERM/SIGINT sent via kill). This code prints RuntimeError: Event loop stopped before Future completed. In the MANY other forms I've tried based on previous answers, I've gotten a plethora of other types of exception classes and error messages with no idea how to fix them. The code above is minimal right now, but some of the attempts I made earlier were anything but, and none of them were correct.
If you're able to modify the script so that it terminates gracefully, an explanation of why your way of doing it is the right way would be greatly appreciated.
Use signal handlers:
import asyncio
from signal import SIGINT, SIGTERM
async def main_coro():
try:
await awaitable()
except asyncio.CancelledError:
do_cleanup()
if __name__ == "__main__":
loop = asyncio.get_event_loop()
main_task = asyncio.ensure_future(main_coro())
for signal in [SIGINT, SIGTERM]:
loop.add_signal_handler(signal, main_task.cancel)
try:
loop.run_until_complete(main_task)
finally:
loop.close()
Stopping the event loop while it is running will never be valid.
Here, you need to catch the Ctrl-C, to indicate to Python that you wish to handle it yourself instead of displaying the default stacktrace. This can be done with a classic try/except:
coro = display_date(loop)
try:
loop.run_until_complete(coro)
except KeyboardInterrupt:
print("Received exit, exiting")
And, for your use-case, that's it!
For a more real-life program, you would probably need to cleanup some resources. See also Graceful shutdown of asyncio coroutines

Categories

Resources