Receiving Unix Signals¶
Unix system event notifications normally interrupt an application,
triggering their handler. When used with asyncio, signal
handler callbacks are interleaved with the other coroutines and
callbacks managed by the event loop. This results in fewer interrupted
functions, and the resulting need to provide safe-guards for cleaning
up incomplete operations.
Signal handlers must be regular callables, not coroutines.
import asyncio
import functools
import os
import signal
def signal_handler(name):
print('signal_handler({!r})'.format(name))
The signal handlers are registered using
add_signal_handler(). The first argument is the signal and the
second is the callback. Callbacks are passed no arguments, so if
arguments are needed a function can be wrapped with
functools.partial().
event_loop = asyncio.get_event_loop()
event_loop.add_signal_handler(
signal.SIGHUP,
functools.partial(signal_handler, name='SIGHUP'),
)
event_loop.add_signal_handler(
signal.SIGUSR1,
functools.partial(signal_handler, name='SIGUSR1'),
)
event_loop.add_signal_handler(
signal.SIGINT,
functools.partial(signal_handler, name='SIGINT'),
)
This example program uses a coroutine to send signals to itself via
os.kill(). After each signal is sent, the coroutine yields
control to allow the handler to be run. In a normal application, there
would be more places where application code yields back to the event
loop and no artificial yield like this would be needed.
async def send_signals():
pid = os.getpid()
print('starting send_signals for {}'.format(pid))
for name in ['SIGHUP', 'SIGHUP', 'SIGUSR1', 'SIGINT']:
print('sending {}'.format(name))
os.kill(pid, getattr(signal, name))
# Yield control to allow the signal handler to run,
# since the signal does not interrupt the program
# flow otherwise.
print('yielding control')
await asyncio.sleep(0.01)
return
The main program runs send_signals() until it has sent all of
the signals.
try:
event_loop.run_until_complete(send_signals())
finally:
event_loop.close()
The output shows how the handlers are called when send_signals()
yields control after sending a signal.
$ python3 asyncio_signal.py
starting send_signals for 21772
sending SIGHUP
yielding control
signal_handler('SIGHUP')
sending SIGHUP
yielding control
signal_handler('SIGHUP')
sending SIGUSR1
yielding control
signal_handler('SIGUSR1')
sending SIGINT
yielding control
signal_handler('SIGINT')
See also
signal– Receive notification of asynchronous system events
PyMOTW-3