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