2005-10-22
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 trys?), 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.)