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.
Run an asyncio Event Loop run_until_complete(<some Future object>) – this function runs a given Future object, usually a coroutine defined by the async / await pattern, until it's complete. run_forever() – this function runs the loop forever. stop() – the stop function stops a running loop.
The asyncio. sleep() method suspends the execution of a coroutine. Coroutines voluntarily yield CPU leading to co-operative multitasking through the await keyword.
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
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