Updated for 2.1b2 distribution.
[python/dscho.git] / Lib / popen2.py
blob8b1af7620ae2064a3ea9cc7d04945518d3b2ee63
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
12 __all__ = ["popen2", "popen3", "popen4"]
14 MAXFD = 256 # Max number of file descriptors (os.getdtablesize()???)
16 _active = []
18 def _cleanup():
19 for inst in _active[:]:
20 inst.poll()
22 class Popen3:
23 """Class representing a child process. Normally instances are created
24 by the factory functions popen2() and popen3()."""
26 sts = -1 # Child not completed yet
28 def __init__(self, cmd, capturestderr=0, bufsize=-1):
29 """The parameter 'cmd' is the shell command to execute in a
30 sub-process. The 'capturestderr' flag, if true, specifies that
31 the object should capture standard error output of the child process.
32 The default is false. If the 'bufsize' parameter is specified, it
33 specifies the size of the I/O buffers to/from the child process."""
34 _cleanup()
35 p2cread, p2cwrite = os.pipe()
36 c2pread, c2pwrite = os.pipe()
37 if capturestderr:
38 errout, errin = os.pipe()
39 self.pid = os.fork()
40 if self.pid == 0:
41 # Child
42 os.dup2(p2cread, 0)
43 os.dup2(c2pwrite, 1)
44 if capturestderr:
45 os.dup2(errin, 2)
46 self._run_child(cmd)
47 os.close(p2cread)
48 self.tochild = os.fdopen(p2cwrite, 'w', bufsize)
49 os.close(c2pwrite)
50 self.fromchild = os.fdopen(c2pread, 'r', bufsize)
51 if capturestderr:
52 os.close(errin)
53 self.childerr = os.fdopen(errout, 'r', bufsize)
54 else:
55 self.childerr = None
56 _active.append(self)
58 def _run_child(self, cmd):
59 if type(cmd) == type(''):
60 cmd = ['/bin/sh', '-c', cmd]
61 for i in range(3, MAXFD):
62 try:
63 os.close(i)
64 except:
65 pass
66 try:
67 os.execvp(cmd[0], cmd)
68 finally:
69 os._exit(1)
71 def poll(self):
72 """Return the exit status of the child process if it has finished,
73 or -1 if it hasn't finished yet."""
74 if self.sts < 0:
75 try:
76 pid, sts = os.waitpid(self.pid, os.WNOHANG)
77 if pid == self.pid:
78 self.sts = sts
79 _active.remove(self)
80 except os.error:
81 pass
82 return self.sts
84 def wait(self):
85 """Wait for and return the exit status of the child process."""
86 pid, sts = os.waitpid(self.pid, 0)
87 if pid == self.pid:
88 self.sts = sts
89 _active.remove(self)
90 return self.sts
93 class Popen4(Popen3):
94 childerr = None
96 def __init__(self, cmd, bufsize=-1):
97 _cleanup()
98 p2cread, p2cwrite = os.pipe()
99 c2pread, c2pwrite = os.pipe()
100 self.pid = os.fork()
101 if self.pid == 0:
102 # Child
103 os.dup2(p2cread, 0)
104 os.dup2(c2pwrite, 1)
105 os.dup2(c2pwrite, 2)
106 self._run_child(cmd)
107 os.close(p2cread)
108 self.tochild = os.fdopen(p2cwrite, 'w', bufsize)
109 os.close(c2pwrite)
110 self.fromchild = os.fdopen(c2pread, 'r', bufsize)
111 _active.append(self)
114 if sys.platform[:3] == "win":
115 # Some things don't make sense on non-Unix platforms.
116 del Popen3, Popen4
118 def popen2(cmd, bufsize=-1, mode='t'):
119 """Execute the shell command 'cmd' in a sub-process. If 'bufsize' is
120 specified, it sets the buffer size for the I/O pipes. The file objects
121 (child_stdout, child_stdin) are returned."""
122 w, r = os.popen2(cmd, mode, bufsize)
123 return r, w
125 def popen3(cmd, bufsize=-1, mode='t'):
126 """Execute the shell command 'cmd' in a sub-process. If 'bufsize' is
127 specified, it sets the buffer size for the I/O pipes. The file objects
128 (child_stdout, child_stdin, child_stderr) are returned."""
129 w, r, e = os.popen3(cmd, mode, bufsize)
130 return r, w, e
132 def popen4(cmd, bufsize=-1, mode='t'):
133 """Execute the shell command 'cmd' in a sub-process. If 'bufsize' is
134 specified, it sets the buffer size for the I/O pipes. The file objects
135 (child_stdout_stderr, child_stdin) are returned."""
136 w, r = os.popen4(cmd, mode, bufsize)
137 return r, w
138 else:
139 def popen2(cmd, bufsize=-1, mode='t'):
140 """Execute the shell command 'cmd' in a sub-process. If 'bufsize' is
141 specified, it sets the buffer size for the I/O pipes. The file objects
142 (child_stdout, child_stdin) are returned."""
143 inst = Popen3(cmd, 0, bufsize)
144 return inst.fromchild, inst.tochild
146 def popen3(cmd, bufsize=-1, mode='t'):
147 """Execute the shell command 'cmd' in a sub-process. If 'bufsize' is
148 specified, it sets the buffer size for the I/O pipes. The file objects
149 (child_stdout, child_stdin, child_stderr) are returned."""
150 inst = Popen3(cmd, 1, bufsize)
151 return inst.fromchild, inst.tochild, inst.childerr
153 def popen4(cmd, bufsize=-1, mode='t'):
154 """Execute the shell command 'cmd' in a sub-process. If 'bufsize' is
155 specified, it sets the buffer size for the I/O pipes. The file objects
156 (child_stdout_stderr, child_stdin) are returned."""
157 inst = Popen4(cmd, bufsize)
158 return inst.fromchild, inst.tochild
160 __all__.extend(["Popen3", "Popen4"])
162 def _test():
163 cmd = "cat"
164 teststr = "ab cd\n"
165 if os.name == "nt":
166 cmd = "more"
167 # "more" doesn't act the same way across Windows flavors,
168 # sometimes adding an extra newline at the start or the
169 # end. So we strip whitespace off both ends for comparison.
170 expected = teststr.strip()
171 print "testing popen2..."
172 r, w = popen2(cmd)
173 w.write(teststr)
174 w.close()
175 got = r.read()
176 if got.strip() != expected:
177 raise ValueError("wrote %s read %s" % (`teststr`, `got`))
178 print "testing popen3..."
179 try:
180 r, w, e = popen3([cmd])
181 except:
182 r, w, e = popen3(cmd)
183 w.write(teststr)
184 w.close()
185 got = r.read()
186 if got.strip() != expected:
187 raise ValueError("wrote %s read %s" % (`teststr`, `got`))
188 got = e.read()
189 if got:
190 raise ValueError("unexected %s on stderr" % `got`)
191 for inst in _active[:]:
192 inst.wait()
193 if _active:
194 raise ValueError("_active not empty")
195 print "All OK"
197 if __name__ == '__main__':
198 _test()