A gotcha with Python and Unix signals

Python likes to handle things through exceptions. As part of this, on Unix it does two important signal changes; it ignores SIGPIPE and catches SIGINT . Each of these can make a Python program report apparent errors where a normal Unix command-line program would just silently exit.

This matters if you want to write a Python program that will play well in an ordinary command-line environment, alongside things like cat , sed , and awk .

First, people expect that they can ^C a Unix command-line program and have it just quietly stop. Python's default behavior turns this into a KeyboardInterrupt exception, which your program is probably not catching; the user will get a multi-line traceback.

Second and more important, Python ignoring SIGPIPE means that your program will get an OSError exception if it writes to a pipe that has closed. Pipes close all the time in Unix command pipelines when you write things like:

generate | mangle.py | head -10

Since head exits after it's read and printed ten lines, further output from mangle.py is probably going to get an OSError . If you didn't handle it (do you guard print statements with try s?), the person running this will see a traceback on standard error. People tend to get irritated when their clean output is messed up with 'error' messages.

( head is not the only program that will do this, and it doesn't necessarily happen all the time. Consider what happens when you feed the output to a pager and quit after seeing the first screen.)

The technique I use for this is:

from signal import signal, \
  SIGPIPE, SIGINT, SIG_DFL, \
  default_int_handler

signal(SIGPIPE, SIG_DFL)
s = signal(SIGINT, SIG_DFL)
if s != default_int_handler:
  signal(SIGINT, s)

Checking what SIGINT is set to is necessary because when your Python program is being run via nohup and similar things, SIGINT will be set to SIG_IGN . If we always set SIGINT to SIG_DFL , we would defeat nohup and irritate the user even more.

(This little thing with SIGINT is not unique to Python; it's something you should watch out for in any program where you're setting a SIGINT handler explicitly. Python itself does it the right way on startup, leaving a SIG_IGN setting alone.)