Re-commit Ping's patch to the cgi and cgitb documentation, using the
[python/dscho.git] / Lib / popen2.py
blob14fe12fe3ba2721fadf98f905057ffe0c07132dc
1 """Spawn a command with pipes to its stdin, stdout, and optionally stderr.
3 The normal os.popen(cmd, mode) call spawns a shell command and provides a
4 file interface to just the input or output of the process depending on
5 whether mode is 'r' or 'w'. This module provides the functions popen2(cmd)
6 and popen3(cmd) which return two or three pipes to the spawned command.
7 """
9 import os
10 import sys
11 import types
13 __all__ = ["popen2", "popen3", "popen4"]
15 MAXFD = 256 # Max number of file descriptors (os.getdtablesize()???)
17 _active = []
19 def _cleanup():
20 for inst in _active[:]:
21 inst.poll()
23 class Popen3:
24 """Class representing a child process. Normally instances are created
25 by the factory functions popen2() and popen3()."""
27 sts = -1 # Child not completed yet
29 def __init__(self, cmd, capturestderr=0, bufsize=-1):
30 """The parameter 'cmd' is the shell command to execute in a
31 sub-process. The 'capturestderr' flag, if true, specifies that
32 the object should capture standard error output of the child process.
33 The default is false. If the 'bufsize' parameter is specified, it
34 specifies the size of the I/O buffers to/from the child process."""
35 _cleanup()
36 p2cread, p2cwrite = os.pipe()
37 c2pread, c2pwrite = os.pipe()
38 if capturestderr:
39 errout, errin = os.pipe()
40 self.pid = os.fork()
41 if self.pid == 0:
42 # Child
43 os.dup2(p2cread, 0)
44 os.dup2(c2pwrite, 1)
45 if capturestderr:
46 os.dup2(errin, 2)
47 self._run_child(cmd)
48 os.close(p2cread)
49 self.tochild = os.fdopen(p2cwrite, 'w', bufsize)
50 os.close(c2pwrite)
51 self.fromchild = os.fdopen(c2pread, 'r', bufsize)
52 if capturestderr:
53 os.close(errin)
54 self.childerr = os.fdopen(errout, 'r', bufsize)
55 else:
56 self.childerr = None
57 _active.append(self)
59 def _run_child(self, cmd):
60 if isinstance(cmd, types.StringTypes):
61 cmd = ['/bin/sh', '-c', cmd]
62 for i in range(3, MAXFD):
63 try:
64 os.close(i)
65 except:
66 pass
67 try:
68 os.execvp(cmd[0], cmd)
69 finally:
70 os._exit(1)
72 def poll(self):
73 """Return the exit status of the child process if it has finished,
74 or -1 if it hasn't finished yet."""
75 if self.sts < 0:
76 try:
77 pid, sts = os.waitpid(self.pid, os.WNOHANG)
78 if pid == self.pid:
79 self.sts = sts
80 _active.remove(self)
81 except os.error:
82 pass
83 return self.sts
85 def wait(self):
86 """Wait for and return the exit status of the child process."""
87 pid, sts = os.waitpid(self.pid, 0)
88 if pid == self.pid:
89 self.sts = sts
90 _active.remove(self)
91 return self.sts
94 class Popen4(Popen3):
95 childerr = None
97 def __init__(self, cmd, bufsize=-1):
98 _cleanup()
99 p2cread, p2cwrite = os.pipe()
100 c2pread, c2pwrite = os.pipe()
101 self.pid = os.fork()
102 if self.pid == 0:
103 # Child
104 os.dup2(p2cread, 0)
105 os.dup2(c2pwrite, 1)
106 os.dup2(c2pwrite, 2)
107 self._run_child(cmd)
108 os.close(p2cread)
109 self.tochild = os.fdopen(p2cwrite, 'w', bufsize)
110 os.close(c2pwrite)
111 self.fromchild = os.fdopen(c2pread, 'r', bufsize)
112 _active.append(self)
115 if sys.platform[:3] == "win":
116 # Some things don't make sense on non-Unix platforms.
117 del Popen3, Popen4
119 def popen2(cmd, bufsize=-1, mode='t'):
120 """Execute the shell command 'cmd' in a sub-process. If 'bufsize' is
121 specified, it sets the buffer size for the I/O pipes. The file objects
122 (child_stdout, child_stdin) are returned."""
123 w, r = os.popen2(cmd, mode, bufsize)
124 return r, w
126 def popen3(cmd, bufsize=-1, mode='t'):
127 """Execute the shell command 'cmd' in a sub-process. If 'bufsize' is
128 specified, it sets the buffer size for the I/O pipes. The file objects
129 (child_stdout, child_stdin, child_stderr) are returned."""
130 w, r, e = os.popen3(cmd, mode, bufsize)
131 return r, w, e
133 def popen4(cmd, bufsize=-1, mode='t'):
134 """Execute the shell command 'cmd' in a sub-process. If 'bufsize' is
135 specified, it sets the buffer size for the I/O pipes. The file objects
136 (child_stdout_stderr, child_stdin) are returned."""
137 w, r = os.popen4(cmd, mode, bufsize)
138 return r, w
139 else:
140 def popen2(cmd, bufsize=-1, mode='t'):
141 """Execute the shell command 'cmd' in a sub-process. If 'bufsize' is
142 specified, it sets the buffer size for the I/O pipes. The file objects
143 (child_stdout, child_stdin) are returned."""
144 inst = Popen3(cmd, 0, bufsize)
145 return inst.fromchild, inst.tochild
147 def popen3(cmd, bufsize=-1, mode='t'):
148 """Execute the shell command 'cmd' in a sub-process. If 'bufsize' is
149 specified, it sets the buffer size for the I/O pipes. The file objects
150 (child_stdout, child_stdin, child_stderr) are returned."""
151 inst = Popen3(cmd, 1, bufsize)
152 return inst.fromchild, inst.tochild, inst.childerr
154 def popen4(cmd, bufsize=-1, mode='t'):
155 """Execute the shell command 'cmd' in a sub-process. If 'bufsize' is
156 specified, it sets the buffer size for the I/O pipes. The file objects
157 (child_stdout_stderr, child_stdin) are returned."""
158 inst = Popen4(cmd, bufsize)
159 return inst.fromchild, inst.tochild
161 __all__.extend(["Popen3", "Popen4"])
163 def _test():
164 cmd = "cat"
165 teststr = "ab cd\n"
166 if os.name == "nt":
167 cmd = "more"
168 # "more" doesn't act the same way across Windows flavors,
169 # sometimes adding an extra newline at the start or the
170 # end. So we strip whitespace off both ends for comparison.
171 expected = teststr.strip()
172 print "testing popen2..."
173 r, w = popen2(cmd)
174 w.write(teststr)
175 w.close()
176 got = r.read()
177 if got.strip() != expected:
178 raise ValueError("wrote %s read %s" % (`teststr`, `got`))
179 print "testing popen3..."
180 try:
181 r, w, e = popen3([cmd])
182 except:
183 r, w, e = popen3(cmd)
184 w.write(teststr)
185 w.close()
186 got = r.read()
187 if got.strip() != expected:
188 raise ValueError("wrote %s read %s" % (`teststr`, `got`))
189 got = e.read()
190 if got:
191 raise ValueError("unexected %s on stderr" % `got`)
192 for inst in _active[:]:
193 inst.wait()
194 if _active:
195 raise ValueError("_active not empty")
196 print "All OK"
198 if __name__ == '__main__':
199 _test()