Issue1652
Created on 2007-12-18 15:41 by cjwatson, last changed 2009-07-03 01:41 by r.david.murray.
|
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.
|
|
| Date |
User |
Action |
Args |
| 2009-07-03 01:41:17 | r.david.murray | set | messages:
+ msg90041 stage: patch review -> test needed |
| 2009-07-03 01:37:58 | r.david.murray | set | versions:
+ Python 2.7, Python 3.2, - Python 2.6 nosy:
+ r.david.murray
messages:
+ msg90040
stage: patch review |
| 2009-07-02 12:20:04 | inducer | set | nosy:
+ inducer
|
| 2009-07-02 09:12:37 | cjwatson | set | messages:
+ msg90004 |
| 2008-04-29 19:44:32 | georg.brandl | set | assignee: loewis messages:
+ msg65979 |
| 2008-04-29 14:53:35 | cjwatson | set | messages:
+ msg65968 |
| 2008-02-12 18:57:18 | loewis | set | priority: high -> normal assignee: loewis -> (no value) messages:
+ msg62327 versions:
+ Python 2.6, - Python 2.5 |
| 2008-02-01 09:41:56 | georg.brandl | set | assignee: loewis messages:
+ msg61953 nosy:
+ loewis |
| 2008-02-01 09:41:06 | cjwatson | set | messages:
+ msg61952 |
| 2008-01-20 14:27:52 | georg.brandl | set | priority: high type: behavior messages:
+ msg61300 severity: normal -> major nosy:
+ georg.brandl |
| 2007-12-18 15:41:06 | cjwatson | create | |
|