source: trunk/essentials/dev-lang/python/Lib/popen2.py

Last change on this file was 3402, checked in by bird, 18 years ago

os2emx -> os2knix.

File size: 9.4 KB
Line 
1"""Spawn a command with pipes to its stdin, stdout, and optionally stderr.
2
3The normal os.popen(cmd, mode) call spawns a shell command and provides a
4file interface to just the input or output of the process depending on
5whether mode is 'r' or 'w'. This module provides the functions popen2(cmd)
6and popen3(cmd) which return two or three pipes to the spawned command.
7"""
8
9import os
10import sys
11
12__all__ = ["popen2", "popen3", "popen4"]
13
14try:
15 MAXFD = os.sysconf('SC_OPEN_MAX')
16except (AttributeError, ValueError):
17 MAXFD = 256
18
19_active = []
20
21def _cleanup():
22 for inst in _active[:]:
23 if inst.poll(_deadstate=sys.maxint) >= 0:
24 try:
25 _active.remove(inst)
26 except ValueError:
27 # This can happen if two threads create a new Popen instance.
28 # It's harmless that it was already removed, so ignore.
29 pass
30
31class Popen3:
32 """Class representing a child process. Normally instances are created
33 by the factory functions popen2() and popen3()."""
34
35 sts = -1 # Child not completed yet
36
37 def __init__(self, cmd, capturestderr=False, bufsize=-1):
38 """The parameter 'cmd' is the shell command to execute in a
39 sub-process. On UNIX, 'cmd' may be a sequence, in which case arguments
40 will be passed directly to the program without shell intervention (as
41 with os.spawnv()). If 'cmd' is a string it will be passed to the shell
42 (as with os.system()). The 'capturestderr' flag, if true, specifies
43 that the object should capture standard error output of the child
44 process. The default is false. If the 'bufsize' parameter is
45 specified, it specifies the size of the I/O buffers to/from the child
46 process."""
47 _cleanup()
48 self.cmd = cmd
49 p2cread, p2cwrite = os.pipe()
50 c2pread, c2pwrite = os.pipe()
51 if capturestderr:
52 errout, errin = os.pipe()
53 self.pid = os.fork()
54 if self.pid == 0:
55 # Child
56 os.dup2(p2cread, 0)
57 os.dup2(c2pwrite, 1)
58 if capturestderr:
59 os.dup2(errin, 2)
60 self._run_child(cmd)
61 os.close(p2cread)
62 self.tochild = os.fdopen(p2cwrite, 'w', bufsize)
63 os.close(c2pwrite)
64 self.fromchild = os.fdopen(c2pread, 'r', bufsize)
65 if capturestderr:
66 os.close(errin)
67 self.childerr = os.fdopen(errout, 'r', bufsize)
68 else:
69 self.childerr = None
70
71 def __del__(self):
72 # In case the child hasn't been waited on, check if it's done.
73 self.poll(_deadstate=sys.maxint)
74 if self.sts < 0:
75 if _active is not None:
76 # Child is still running, keep us alive until we can wait on it.
77 _active.append(self)
78
79 def _run_child(self, cmd):
80 if isinstance(cmd, basestring):
81 cmd = ['/bin/sh', '-c', cmd]
82 for i in xrange(3, MAXFD):
83 try:
84 os.close(i)
85 except OSError:
86 pass
87 try:
88 os.execvp(cmd[0], cmd)
89 finally:
90 os._exit(1)
91
92 def poll(self, _deadstate=None):
93 """Return the exit status of the child process if it has finished,
94 or -1 if it hasn't finished yet."""
95 if self.sts < 0:
96 try:
97 pid, sts = os.waitpid(self.pid, os.WNOHANG)
98 # pid will be 0 if self.pid hasn't terminated
99 if pid == self.pid:
100 self.sts = sts
101 except os.error:
102 if _deadstate is not None:
103 self.sts = _deadstate
104 return self.sts
105
106 def wait(self):
107 """Wait for and return the exit status of the child process."""
108 if self.sts < 0:
109 pid, sts = os.waitpid(self.pid, 0)
110 # This used to be a test, but it is believed to be
111 # always true, so I changed it to an assertion - mvl
112 assert pid == self.pid
113 self.sts = sts
114 return self.sts
115
116
117class Popen4(Popen3):
118 childerr = None
119
120 def __init__(self, cmd, bufsize=-1):
121 _cleanup()
122 self.cmd = cmd
123 p2cread, p2cwrite = os.pipe()
124 c2pread, c2pwrite = os.pipe()
125 self.pid = os.fork()
126 if self.pid == 0:
127 # Child
128 os.dup2(p2cread, 0)
129 os.dup2(c2pwrite, 1)
130 os.dup2(c2pwrite, 2)
131 self._run_child(cmd)
132 os.close(p2cread)
133 self.tochild = os.fdopen(p2cwrite, 'w', bufsize)
134 os.close(c2pwrite)
135 self.fromchild = os.fdopen(c2pread, 'r', bufsize)
136
137
138if sys.platform[:3] == "win" or sys.platform == "os2emx" or sys.platform == "os2knix":
139 # Some things don't make sense on non-Unix platforms.
140 del Popen3, Popen4
141
142 def popen2(cmd, bufsize=-1, mode='t'):
143 """Execute the shell command 'cmd' in a sub-process. On UNIX, 'cmd' may
144 be a sequence, in which case arguments will be passed directly to the
145 program without shell intervention (as with os.spawnv()). If 'cmd' is a
146 string it will be passed to the shell (as with os.system()). If
147 'bufsize' is specified, it sets the buffer size for the I/O pipes. The
148 file objects (child_stdout, child_stdin) are returned."""
149 w, r = os.popen2(cmd, mode, bufsize)
150 return r, w
151
152 def popen3(cmd, bufsize=-1, mode='t'):
153 """Execute the shell command 'cmd' in a sub-process. On UNIX, 'cmd' may
154 be a sequence, in which case arguments will be passed directly to the
155 program without shell intervention (as with os.spawnv()). If 'cmd' is a
156 string it will be passed to the shell (as with os.system()). If
157 'bufsize' is specified, it sets the buffer size for the I/O pipes. The
158 file objects (child_stdout, child_stdin, child_stderr) are returned."""
159 w, r, e = os.popen3(cmd, mode, bufsize)
160 return r, w, e
161
162 def popen4(cmd, bufsize=-1, mode='t'):
163 """Execute the shell command 'cmd' in a sub-process. On UNIX, 'cmd' may
164 be a sequence, in which case arguments will be passed directly to the
165 program without shell intervention (as with os.spawnv()). If 'cmd' is a
166 string it will be passed to the shell (as with os.system()). If
167 'bufsize' is specified, it sets the buffer size for the I/O pipes. The
168 file objects (child_stdout_stderr, child_stdin) are returned."""
169 w, r = os.popen4(cmd, mode, bufsize)
170 return r, w
171else:
172 def popen2(cmd, bufsize=-1, mode='t'):
173 """Execute the shell command 'cmd' in a sub-process. On UNIX, 'cmd' may
174 be a sequence, in which case arguments will be passed directly to the
175 program without shell intervention (as with os.spawnv()). If 'cmd' is a
176 string it will be passed to the shell (as with os.system()). If
177 'bufsize' is specified, it sets the buffer size for the I/O pipes. The
178 file objects (child_stdout, child_stdin) are returned."""
179 inst = Popen3(cmd, False, bufsize)
180 return inst.fromchild, inst.tochild
181
182 def popen3(cmd, bufsize=-1, mode='t'):
183 """Execute the shell command 'cmd' in a sub-process. On UNIX, 'cmd' may
184 be a sequence, in which case arguments will be passed directly to the
185 program without shell intervention (as with os.spawnv()). If 'cmd' is a
186 string it will be passed to the shell (as with os.system()). If
187 'bufsize' is specified, it sets the buffer size for the I/O pipes. The
188 file objects (child_stdout, child_stdin, child_stderr) are returned."""
189 inst = Popen3(cmd, True, bufsize)
190 return inst.fromchild, inst.tochild, inst.childerr
191
192 def popen4(cmd, bufsize=-1, mode='t'):
193 """Execute the shell command 'cmd' in a sub-process. On UNIX, 'cmd' may
194 be a sequence, in which case arguments will be passed directly to the
195 program without shell intervention (as with os.spawnv()). If 'cmd' is a
196 string it will be passed to the shell (as with os.system()). If
197 'bufsize' is specified, it sets the buffer size for the I/O pipes. The
198 file objects (child_stdout_stderr, child_stdin) are returned."""
199 inst = Popen4(cmd, bufsize)
200 return inst.fromchild, inst.tochild
201
202 __all__.extend(["Popen3", "Popen4"])
203
204def _test():
205 # When the test runs, there shouldn't be any open pipes
206 _cleanup()
207 assert not _active, "Active pipes when test starts " + repr([c.cmd for c in _active])
208 cmd = "cat"
209 teststr = "ab cd\n"
210 if os.name == "nt":
211 cmd = "more"
212 # "more" doesn't act the same way across Windows flavors,
213 # sometimes adding an extra newline at the start or the
214 # end. So we strip whitespace off both ends for comparison.
215 expected = teststr.strip()
216 print "testing popen2..."
217 r, w = popen2(cmd)
218 w.write(teststr)
219 w.close()
220 got = r.read()
221 if got.strip() != expected:
222 raise ValueError("wrote %r read %r" % (teststr, got))
223 print "testing popen3..."
224 try:
225 r, w, e = popen3([cmd])
226 except:
227 r, w, e = popen3(cmd)
228 w.write(teststr)
229 w.close()
230 got = r.read()
231 if got.strip() != expected:
232 raise ValueError("wrote %r read %r" % (teststr, got))
233 got = e.read()
234 if got:
235 raise ValueError("unexpected %r on stderr" % (got,))
236 for inst in _active[:]:
237 inst.wait()
238 _cleanup()
239 if _active:
240 raise ValueError("_active not empty")
241 print "All OK"
242
243if __name__ == '__main__':
244 _test()
Note: See TracBrowser for help on using the repository browser.