Index: Doc/library/subprocess.rst =================================================================== --- Doc/library/subprocess.rst (revision 59547) +++ Doc/library/subprocess.rst (working copy) @@ -30,7 +30,7 @@ This module defines one class called :class:`Popen`: -.. class:: Popen(args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0) +.. class:: Popen(args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0, restore_sigpipe=False) Arguments are: @@ -112,6 +112,12 @@ underlying CreateProcess() function. They can specify things such as appearance of the main window and priority for the new process. (Windows only) + On UNIX: If *restore_sigpipe* is true, :const:`SIGPIPE` will be restored + to its default action, which is typically to terminate the process. This + is appropriate for non-Python subprocesses, which may otherwise fail to + notice write errors due to the reading end of a pipe going away, or + produce mysterious "Broken pipe" errors. + Convenience Functions ^^^^^^^^^^^^^^^^^^^^^ @@ -259,7 +265,7 @@ output=`mycmd myarg` ==> - output = Popen(["mycmd", "myarg"], stdout=PIPE).communicate()[0] + output = Popen(["mycmd", "myarg"], stdout=PIPE, restore_sigpipe=True).communicate()[0] Replacing shell pipe line @@ -269,7 +275,7 @@ output=`dmesg | grep hda` ==> - p1 = Popen(["dmesg"], stdout=PIPE) + p1 = Popen(["dmesg"], stdout=PIPE, restore_sigpipe=True) p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE) output = p2.communicate()[0] @@ -281,7 +287,7 @@ sts = os.system("mycmd" + " myarg") ==> - p = Popen("mycmd" + " myarg", shell=True) + p = Popen("mycmd" + " myarg", shell=True, restore_sigpipe=True) sts = os.waitpid(p.pid, 0) Notes: @@ -293,7 +299,7 @@ A more realistic example would look like this:: try: - retcode = call("mycmd" + " myarg", shell=True) + retcode = call("mycmd" + " myarg", shell=True, restore_sigpipe=True) if retcode < 0: print >>sys.stderr, "Child was terminated by signal", -retcode else: @@ -309,25 +315,26 @@ pid = os.spawnlp(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg") ==> - pid = Popen(["/bin/mycmd", "myarg"]).pid + pid = Popen(["/bin/mycmd", "myarg"], restore_sigpipe=True).pid P_WAIT example:: retcode = os.spawnlp(os.P_WAIT, "/bin/mycmd", "mycmd", "myarg") ==> - retcode = call(["/bin/mycmd", "myarg"]) + retcode = call(["/bin/mycmd", "myarg"], restore_sigpipe=True) Vector example:: os.spawnvp(os.P_NOWAIT, path, args) ==> - Popen([path] + args[1:]) + Popen([path] + args[1:], restore_sigpipe=True) Environment example:: os.spawnlpe(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg", env) ==> - Popen(["/bin/mycmd", "myarg"], env={"PATH": "/usr/bin"}) + Popen(["/bin/mycmd", "myarg"], env={"PATH": "/usr/bin"}, + restore_sigpipe=True) Replacing os.popen\* @@ -337,20 +344,23 @@ pipe = os.popen(cmd, mode='r', bufsize) ==> - pipe = Popen(cmd, shell=True, bufsize=bufsize, stdout=PIPE).stdout + pipe = Popen(cmd, shell=True, bufsize=bufsize, stdout=PIPE, + restore_sigpipe=True).stdout :: pipe = os.popen(cmd, mode='w', bufsize) ==> - pipe = Popen(cmd, shell=True, bufsize=bufsize, stdin=PIPE).stdin + pipe = Popen(cmd, shell=True, bufsize=bufsize, stdin=PIPE, + restore_sigpipe=True).stdin :: (child_stdin, child_stdout) = os.popen2(cmd, mode, bufsize) ==> p = Popen(cmd, shell=True, bufsize=bufsize, - stdin=PIPE, stdout=PIPE, close_fds=True) + stdin=PIPE, stdout=PIPE, close_fds=True, + restore_sigpipe=True) (child_stdin, child_stdout) = (p.stdin, p.stdout) :: @@ -360,7 +370,8 @@ child_stderr) = os.popen3(cmd, mode, bufsize) ==> p = Popen(cmd, shell=True, bufsize=bufsize, - stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) + stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True, + restore_sigpipe=True) (child_stdin, child_stdout, child_stderr) = (p.stdin, p.stdout, p.stderr) @@ -370,7 +381,8 @@ (child_stdin, child_stdout_and_stderr) = os.popen4(cmd, mode, bufsize) ==> p = Popen(cmd, shell=True, bufsize=bufsize, - stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True) + stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True, + restore_sigpipe=True) (child_stdin, child_stdout_and_stderr) = (p.stdin, p.stdout) @@ -387,7 +399,8 @@ (child_stdout, child_stdin) = popen2.popen2("somestring", bufsize, mode) ==> p = Popen(["somestring"], shell=True, bufsize=bufsize, - stdin=PIPE, stdout=PIPE, close_fds=True) + stdin=PIPE, stdout=PIPE, close_fds=True, + restore_sigpipe=True) (child_stdout, child_stdin) = (p.stdout, p.stdin) :: @@ -395,7 +408,8 @@ (child_stdout, child_stdin) = popen2.popen2(["mycmd", "myarg"], bufsize, mode) ==> p = Popen(["mycmd", "myarg"], bufsize=bufsize, - stdin=PIPE, stdout=PIPE, close_fds=True) + stdin=PIPE, stdout=PIPE, close_fds=True, + restore_sigpipe=True) (child_stdout, child_stdin) = (p.stdout, p.stdin) The popen2.Popen3 and popen2.Popen4 basically works as subprocess.Popen, except Index: Lib/subprocess.py =================================================================== --- Lib/subprocess.py (revision 59547) +++ Lib/subprocess.py (working copy) @@ -34,7 +34,7 @@ stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, - startupinfo=None, creationflags=0): + startupinfo=None, creationflags=0, restore_sigpipe=False): Arguments are: @@ -106,7 +106,13 @@ appearance of the main window and priority for the new process. (Windows only) +On UNIX: If restore_sigpipe is true, SIGPIPE will be restored to its +default action, which is typically to terminate the process. This is +appropriate for non-Python subprocesses, which may otherwise fail to +notice write errors due to the reading end of a pipe going away, or +produce mysterious "Broken pipe" errors. + This module also defines two shortcut functions: call(*popenargs, **kwargs): @@ -217,14 +223,14 @@ --------------------------------- output=`mycmd myarg` ==> -output = Popen(["mycmd", "myarg"], stdout=PIPE).communicate()[0] +output = Popen(["mycmd", "myarg"], stdout=PIPE, restore_sigpipe=True).communicate()[0] Replacing shell pipe line ------------------------- output=`dmesg | grep hda` ==> -p1 = Popen(["dmesg"], stdout=PIPE) +p1 = Popen(["dmesg"], stdout=PIPE, restore_sigpipe=True) p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE) output = p2.communicate()[0] @@ -233,7 +239,7 @@ --------------------- sts = os.system("mycmd" + " myarg") ==> -p = Popen("mycmd" + " myarg", shell=True) +p = Popen("mycmd" + " myarg", shell=True, restore_sigpipe=True) pid, sts = os.waitpid(p.pid, 0) Note: @@ -246,7 +252,7 @@ A more real-world example would look like this: try: - retcode = call("mycmd" + " myarg", shell=True) + retcode = call("mycmd" + " myarg", shell=True, restore_sigpipe=True) if retcode < 0: print >>sys.stderr, "Child was terminated by signal", -retcode else: @@ -261,45 +267,49 @@ pid = os.spawnlp(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg") ==> -pid = Popen(["/bin/mycmd", "myarg"]).pid +pid = Popen(["/bin/mycmd", "myarg"], restore_sigpipe=True).pid P_WAIT example: retcode = os.spawnlp(os.P_WAIT, "/bin/mycmd", "mycmd", "myarg") ==> -retcode = call(["/bin/mycmd", "myarg"]) +retcode = call(["/bin/mycmd", "myarg"], restore_sigpipe=True) Vector example: os.spawnvp(os.P_NOWAIT, path, args) ==> -Popen([path] + args[1:]) +Popen([path] + args[1:], restore_sigpipe=True) Environment example: os.spawnlpe(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg", env) ==> -Popen(["/bin/mycmd", "myarg"], env={"PATH": "/usr/bin"}) +Popen(["/bin/mycmd", "myarg"], env={"PATH": "/usr/bin"}, + restore_sigpipe=True) Replacing os.popen* ------------------- pipe = os.popen(cmd, mode='r', bufsize) ==> -pipe = Popen(cmd, shell=True, bufsize=bufsize, stdout=PIPE).stdout +pipe = Popen(cmd, shell=True, bufsize=bufsize, stdout=PIPE, + restore_sigpipe=True).stdout pipe = os.popen(cmd, mode='w', bufsize) ==> -pipe = Popen(cmd, shell=True, bufsize=bufsize, stdin=PIPE).stdin +pipe = Popen(cmd, shell=True, bufsize=bufsize, stdin=PIPE, + restore_sigpipe=True).stdin (child_stdin, child_stdout) = os.popen2(cmd, mode, bufsize) ==> p = Popen(cmd, shell=True, bufsize=bufsize, - stdin=PIPE, stdout=PIPE, close_fds=True) + stdin=PIPE, stdout=PIPE, close_fds=True, + restore_sigpipe=True) (child_stdin, child_stdout) = (p.stdin, p.stdout) @@ -308,7 +318,8 @@ child_stderr) = os.popen3(cmd, mode, bufsize) ==> p = Popen(cmd, shell=True, bufsize=bufsize, - stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) + stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True, + restore_sigpipe=True) (child_stdin, child_stdout, child_stderr) = (p.stdin, p.stdout, p.stderr) @@ -317,7 +328,8 @@ (child_stdin, child_stdout_and_stderr) = os.popen4(cmd, mode, bufsize) ==> p = Popen(cmd, shell=True, bufsize=bufsize, - stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True) + stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True, + restore_sigpipe=True) (child_stdin, child_stdout_and_stderr) = (p.stdin, p.stdout) @@ -330,14 +342,16 @@ (child_stdout, child_stdin) = popen2.popen2("somestring", bufsize, mode) ==> p = Popen(["somestring"], shell=True, bufsize=bufsize - stdin=PIPE, stdout=PIPE, close_fds=True) + stdin=PIPE, stdout=PIPE, close_fds=True, + restore_sigpipe=True) (child_stdout, child_stdin) = (p.stdout, p.stdin) (child_stdout, child_stdin) = popen2.popen2(["mycmd", "myarg"], bufsize, mode) ==> p = Popen(["mycmd", "myarg"], bufsize=bufsize, - stdin=PIPE, stdout=PIPE, close_fds=True) + stdin=PIPE, stdout=PIPE, close_fds=True, + restore_sigpipe=True) (child_stdout, child_stdin) = (p.stdout, p.stdin) The popen2.Popen3 and popen2.Popen4 basically works as subprocess.Popen, @@ -533,7 +547,7 @@ stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, - startupinfo=None, creationflags=0): + startupinfo=None, creationflags=0, restore_sigpipe=False): """Create new Popen instance.""" _cleanup() @@ -586,7 +600,7 @@ self._execute_child(args, executable, preexec_fn, close_fds, cwd, env, universal_newlines, - startupinfo, creationflags, shell, + startupinfo, creationflags, shell, restore_sigpipe, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite) @@ -761,7 +775,7 @@ def _execute_child(self, args, executable, preexec_fn, close_fds, cwd, env, universal_newlines, - startupinfo, creationflags, shell, + startupinfo, creationflags, shell, restore_sigpipe, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite): @@ -976,7 +990,7 @@ def _execute_child(self, args, executable, preexec_fn, close_fds, cwd, env, universal_newlines, - startupinfo, creationflags, shell, + startupinfo, creationflags, shell, restore_sigpipe, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite): @@ -1037,6 +1051,10 @@ if cwd is not None: os.chdir(cwd) + if restore_sigpipe: + import signal + signal.signal(signal.SIGPIPE, signal.SIG_DFL) + if preexec_fn: apply(preexec_fn) @@ -1197,7 +1215,7 @@ # Example 3: Connecting several subprocesses # print "Looking for 'hda'..." - p1 = Popen(["dmesg"], stdout=PIPE) + p1 = Popen(["dmesg"], stdout=PIPE, restore_sigpipe=True) p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE) print repr(p2.communicate()[0])