This article covers signalfd, a system call only available on Linux. If anyone knows of an equivalent for OSX or BSDs,
Writing asynchronous IO code is fun; handling signals is not. signalfd allows you to move your signal handling code into your main event loop instead of hooking up global handlers and using the featureless set_wakeup_fd function to break the main loop.
Luckily Jean-Paul Calderone had already created a great Python wrapper for the signalfd and sigprocmask system calls. Unfortunately it doesn’t include a way to parse the siginfo_t structure which contains all the useful information about the signal you’re handling.
I’ve added a helper to do just that in a branch:
A sample program would look like:
import os import select import signal import signalfd def sigfdtest(): # Catch them all! sigs =  for attr in dir(signal): if attr.startswith('SIG') and not attr.startswith('SIG_'): sigs.append(getattr(signal, attr)) sfd = signalfd.create_signalfd(sigs) print 'Capturing: %r' % sorted(sigs) while 1: print 'selecting - pid: %d' % os.getpid() r = select.select([sfd], , ) for s in r: assert s is sfd, 'Python nicely re-uses the fd instance' sig = signalfd.read_signalfd(sfd) print sig if __name__ == '__main__': sigfdtest()
When run you can throw some signals at it:
Capturing: [6, 14, 7, 17, 17, 18, 8, 1, 4, 2, 29, 6, 9, 13, 29, 27, 30, 3, 64, 34, 11, 19, 31, 15, 5, 20, 21, 22, 23, 10, 12, 26, 28, 24, 25] selecting - pid: 6523 ^CSIGINT selecting - pid: 6523 ^CSIGINT selecting - pid: 6523 SIGHUP selecting - pid: 6523 Killed
Of course you’ll need to use an uninterpretable signal like KILL to exit.
Jean-Paul attempted to get signalfd included in Python 2.7’s signal module, and it was slated for inclusion in 3.2. However, given that 3.2 was just released without it, I’m guessing the attempt to get this functionality into Python’s stdlib has been forgotten.
Up next: eventfd perhaps?