|
1 """Spawn a command with pipes to its stdin, stdout, and optionally stderr. |
|
2 |
|
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 """ |
|
8 |
|
9 import os |
|
10 import sys |
|
11 |
|
12 __all__ = ["popen2", "popen3", "popen4"] |
|
13 |
|
14 try: |
|
15 MAXFD = os.sysconf('SC_OPEN_MAX') |
|
16 except (AttributeError, ValueError): |
|
17 MAXFD = 256 |
|
18 |
|
19 _active = [] |
|
20 |
|
21 def _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 |
|
31 class 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 |
|
117 class 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 |
|
138 if sys.platform[:3] == "win" or sys.platform == "os2emx": |
|
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 |
|
171 else: |
|
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 |
|
204 def _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 |
|
243 if __name__ == '__main__': |
|
244 _test() |