classification
Title: subprocess should have an option to restore SIGPIPE to default action
Type: behavior Stage: test needed
Components: Library (Lib) Versions: Python 3.2, Python 2.7
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: loewis Nosy List: cjwatson, georg.brandl, inducer, loewis, r.david.murray (5)
Priority: normal Keywords

Created on 2007-12-18 15:41 by cjwatson, last changed 2009-07-03 01:41 by r.david.murray.

Files
File name Uploaded Description Edit Remove
subprocess-sigpipe.patch cjwatson, 2007-12-18 15:41
Messages (10)
msg58750 - (view) Author: Colin Watson (cjwatson) Date: 2007-12-18 15:41
On Unix, Python sets SIGPIPE to SIG_IGN on startup, because it prefers
to check every write and raise an IOError exception rather than taking
SIGPIPE. This is all well and good for Python itself. However,
non-Python Unix subprocesses generally expect to have SIGPIPE set to the
default action, since that's what will happen if they're started from a
normal shell. If this is not the case, then rather than taking SIGPIPE
when a write fails due to the reading end of a pipe having been closed,
write will return EPIPE and it's up to the process to check that. Many
programs are lazy and fail to check for write errors, but in the
specific case of pipe closure they are fine when invoked from a normal
shell because they rely on taking the signal. Even correctly written
programs that diligently check for write errors will typically produce
different (and confusing) output when SIGPIPE is ignored. For instance,
an example only very slightly adapted from one in the subprocess
documentation:

  $ dd if=/dev/zero of=bigfile bs=1024 seek=10000 count=1
  1+0 records in
  1+0 records out
  1024 bytes (1.0 kB) copied, 0.000176709 seconds, 5.8 MB/s
  $ cat bigfile | head -n0
  $ cat t.py
  from subprocess import Popen, PIPE
  p1 = Popen(["cat", "bigfile"], stdout=PIPE)
  p2 = Popen(["head", "-n0"], stdin=p1.stdout, stdout=PIPE)
  output = p2.communicate()[0]
  $ python t.py
  cat: write error: Broken pipe

In some cases this problem can be much more significant. For instance,
it is very common for shell scripts to rely on SIGPIPE's default action
in order to behave correctly. A year or two ago I ran into this in OS
installer code I was writing in Python, which called some underlying
utility code in shell and C to deal with disk partitioning. In the event
that the Python code failed to handle an exception, the shell script
being run in a subprocess would spiral out of control rather than
cleanly exiting at the first sign of trouble. This actually caused
massive data loss on several testers' systems and required a quick
release to fix
(https://bugs.launchpad.net/ubuntu/+source/ubiquity/+bug/40464 and
https://wiki.ubuntu.com/DapperBeta/PartitionTableCorruption). Now
obviously this was ultimately my fault for failing to catch the
exceptional condition in testing, but this misdesign in Python and the
lack of any documentation of this gotcha really didn't help at the time.
For the record, the fix was to call this in a preexec_fn:

  signal.signal(signal.SIGPIPE, signal.SIG_DFL)

This is an area of Unix that's very easy to get wrong. I've written my
own subprocess handling library in C for another project, and I still
found it non-trivial to track this down when it bit me. Since it
essentially arises due to an implementation detail of the Python
language, I think it should also be Python's responsibility to fix it up
when subprocesses are involved.

There are many ways to invoke subprocesses in Python, but the new,
all-singing, all-dancing one is of course the subprocess module. I think
it would be sufficient for that to do the right thing, or at least have
an option to do so, and it's certainly the easiest place to add the
option. I'm attaching a suggested patch which adds a restore_sigpipe
option and documents it in what seem to be all the relevant places.

Note that nearly all the examples end up with restore_sigpipe enabled.
In some future release of Python, I think this should be the default.
I'm not entirely familiar with migration plan processes in Python; how
should this be done?
msg61300 - (view) Author: Georg Brandl (georg.brandl) Date: 2008-01-20 14:27
Raising priority.

What incompatibilities could occur if SIGPIPE is restored by default?
msg61952 - (view) Author: Colin Watson (cjwatson) Date: 2008-02-01 09:41
To be quite honest I can't think of any incompatibilities that wouldn't
have the basic result of improving matters. I put the migration stuff in
my bug report in case somebody else could, because I don't want the bug
fix to stall on that.

My preference would be for Python to move to behaviour equivalent to
restore_sigpipe=True in the next release, but I would rather that it
gained restore_sigpipe with the wrong default than that it didn't gain
it at all.
msg61953 - (view) Author: Georg Brandl (georg.brandl) Date: 2008-02-01 09:41
Martin, what do you think?
msg62327 - (view) Author: Martin v. Löwis (loewis) Date: 2008-02-12 18:57
The patch as it stands (subprocess-sigpipe.patch) definitely can't go
into 2.5.x: it introduces a new feature.

It's not clear to me whether Colin intended to target it for 2.5.x, as
it is against the trunk. For the trunk, the patch is fine.

Regargeting for 2.6. Colin, if that wasn't your intention, please speak up.
msg65968 - (view) Author: Colin Watson (cjwatson) Date: 2008-04-29 14:53
2.6 is fine if that's what the release process dictates; I don't want it
to be lost, that's all.
msg65979 - (view) Author: Georg Brandl (georg.brandl) Date: 2008-04-29 19:44
Martin, what do you think?
msg90004 - (view) Author: Colin Watson (cjwatson) Date: 2009-07-02 09:12
Is there anything more I can do to move this along? Thanks.
msg90040 - (view) Author: R. David Murray (r.david.murray) Date: 2009-07-03 01:37
Find someone to review the patch and post their evaluation here.  It
doesn't have to be a committer, though that would be even better.
msg90041 - (view) Author: R. David Murray (r.david.murray) Date: 2009-07-03 01:41
Hmm.  Looks like the patch is missing a unit test for the new feature.
History
Date User Action Args
2009-07-03 01:41:17r.david.murraysetmessages: + msg90041
stage: patch review -> test needed
2009-07-03 01:37:58r.david.murraysetversions: + Python 2.7, Python 3.2, - Python 2.6
nosy: + r.david.murray

messages: + msg90040

stage: patch review
2009-07-02 12:20:04inducersetnosy: + inducer
2009-07-02 09:12:37cjwatsonsetmessages: + msg90004
2008-04-29 19:44:32georg.brandlsetassignee: loewis
messages: + msg65979
2008-04-29 14:53:35cjwatsonsetmessages: + msg65968
2008-02-12 18:57:18loewissetpriority: high -> normal
assignee: loewis -> (no value)
messages: + msg62327
versions: + Python 2.6, - Python 2.5
2008-02-01 09:41:56georg.brandlsetassignee: loewis
messages: + msg61953
nosy: + loewis
2008-02-01 09:41:06cjwatsonsetmessages: + msg61952
2008-01-20 14:27:52georg.brandlsetpriority: high
type: behavior
messages: + msg61300
severity: normal -> major
nosy: + georg.brandl
2007-12-18 15:41:06cjwatsoncreate