Reputation: 327
I'm currently porting a Python project from Linux to Windows (using Anaconda Python 3.6). Everything works perfectly, I just cannot get a graceful exit of the asyncio loop working.
In Linux I'm doing the following:
class GracefulExit(SystemExit):
code = 1
def raise_graceful_exit():
raise GracefulExit()
loop = asyncio.get_event_loop()
loop.add_signal_handler(signal.SIGINT, raise_graceful_exit)
loop.add_signal_handler(signal.SIGTERM, raise_graceful_exit)
try:
loop.run_forever()
except GracefulExit:
pass
shutdown()
In Windows, unfortunately I get a NotImplementedError
on add_signal_handler
. Without this, of course I never get a chance for a clean shutdown of the program.
Any ideas on how to solve this? Thanks.
Upvotes: 13
Views: 13747
Reputation: 2385
yet another version of multiplatform graceful exit
import asyncio
import signal
async def main():
try:
i=0
while i<10:
print('<Your app is running>')
await asyncio.sleep(1)
i +=1
except asyncio.CancelledError:
for i in range(3):
print('<Your app is shutting down...>')
await asyncio.sleep(1)
if __name__ == '__main__':
class GracefulExit(SystemExit):
code = 1
def raise_graceful_exit(*args):
tasks = asyncio.all_tasks(loop=loop)
for t in tasks:
t.cancel()
loop.stop()
print("Gracefully shutdown")
raise GracefulExit()
def do_something():
while True:
pass
loop = asyncio.get_event_loop()
signal.signal(signal.SIGINT, raise_graceful_exit)
signal.signal(signal.SIGTERM, raise_graceful_exit)
task = loop.create_task(main())
try:
loop.run_until_complete(task)
except GracefulExit:
print('Got signal: SIGINT, shutting down.')
if 1:
tasks = asyncio.all_tasks(loop=loop)
for t in tasks:
t.cancel()
group = asyncio.gather(*tasks, return_exceptions=True)
loop.run_until_complete(group)
loop.close()
Upvotes: 2
Reputation: 1074
Actually you can implement kind of a cross-platform python signal handler in your python script that works on both Unix/Linux and Windows, python has a standard signal library, so you can do something like this
import asyncio
import signal
class GracefulExit(SystemExit):
code = 1
def raise_graceful_exit(*args):
loop.stop()
print("Gracefully shutdown")
raise GracefulExit()
def do_something():
while True:
pass
loop = asyncio.get_event_loop()
signal.signal(signal.SIGINT, raise_graceful_exit)
signal.signal(signal.SIGTERM, raise_graceful_exit)
try:
loop.run_forever(do_something())
except GracefulExit:
pass
finally:
loop.close()
It does not behave exactly the same on Windows and Linux due to the aforementioned platform differences, but for most cases it works okay on both platforms.
Upvotes: 7
Reputation: 7257
Windows does not have signals.
If the process gets killed via the TerminateProcess API, you do not get any chance to cleanup (its like 'kill -9', blows your process away).
But windows has two ways to signal if your code should exit, one for console programs (e.g. python.exe) and one for gui programs (pythonw.exe).
Python automatically handles the console thing and raises a KeyboardInterrupt exception, but you can hook your own code into that handler with the console control handler APIs (https://learn.microsoft.com/en-us/windows/console/console-control-handlers), but thats probably overkill. Just set a proper exception handler on your event loop.
For GUI processes, Windows sends you windows messages, like WM_QUIT or various others if a user logs off, system goes to power saving mode etc., you can handle those too, with the help of ctypes or the win32 package, or any of the many python gui libs.
Upvotes: 10