Quick update to the README file. For intros and books we now point to
[python/dscho.git] / Lib / popen2.py
blobeb8fb9ad1fcdf0b6a0a13ac5e644575aaf4b4163
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 string
13 MAXFD = 256 # Max number of file descriptors (os.getdtablesize()???)
15 _active = []
17 def _cleanup():
18 for inst in _active[:]:
19 inst.poll()
21 class Popen3:
22 """Class representing a child process. Normally instances are created
23 by the factory functions popen2() and popen3()."""
25 def __init__(self, cmd, capturestderr=0, bufsize=-1):
26 """The parameter 'cmd' is the shell command to execute in a
27 sub-process. The 'capturestderr' flag, if true, specifies that
28 the object should capture standard error output of the child process.
29 The default is false. If the 'bufsize' parameter is specified, it
30 specifies the size of the I/O buffers to/from the child process."""
31 if type(cmd) == type(''):
32 cmd = ['/bin/sh', '-c', cmd]
33 p2cread, p2cwrite = os.pipe()
34 c2pread, c2pwrite = os.pipe()
35 if capturestderr:
36 errout, errin = os.pipe()
37 self.pid = os.fork()
38 if self.pid == 0:
39 # Child
40 os.close(0)
41 os.close(1)
42 if os.dup(p2cread) <> 0:
43 sys.stderr.write('popen2: bad read dup\n')
44 if os.dup(c2pwrite) <> 1:
45 sys.stderr.write('popen2: bad write dup\n')
46 if capturestderr:
47 os.close(2)
48 if os.dup(errin) <> 2: pass
49 for i in range(3, MAXFD):
50 try:
51 os.close(i)
52 except: pass
53 try:
54 os.execvp(cmd[0], cmd)
55 finally:
56 os._exit(1)
57 # Shouldn't come here, I guess
58 os._exit(1)
59 os.close(p2cread)
60 self.tochild = os.fdopen(p2cwrite, 'w', bufsize)
61 os.close(c2pwrite)
62 self.fromchild = os.fdopen(c2pread, 'r', bufsize)
63 if capturestderr:
64 os.close(errin)
65 self.childerr = os.fdopen(errout, 'r', bufsize)
66 else:
67 self.childerr = None
68 self.sts = -1 # Child not completed yet
69 _active.append(self)
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
92 def popen2(cmd, bufsize=-1):
93 """Execute the shell command 'cmd' in a sub-process. If 'bufsize' is
94 specified, it sets the buffer size for the I/O pipes. The file objects
95 (child_stdout, child_stdin) are returned."""
96 _cleanup()
97 inst = Popen3(cmd, 0, bufsize)
98 return inst.fromchild, inst.tochild
100 def popen3(cmd, bufsize=-1):
101 """Execute the shell command 'cmd' in a sub-process. If 'bufsize' is
102 specified, it sets the buffer size for the I/O pipes. The file objects
103 (child_stdout, child_stdin, child_stderr) are returned."""
104 _cleanup()
105 inst = Popen3(cmd, 1, bufsize)
106 return inst.fromchild, inst.tochild, inst.childerr
108 def _test():
109 teststr = "abc\n"
110 print "testing popen2..."
111 r, w = popen2('cat')
112 w.write(teststr)
113 w.close()
114 assert r.read() == teststr
115 print "testing popen3..."
116 r, w, e = popen3(['cat'])
117 w.write(teststr)
118 w.close()
119 assert r.read() == teststr
120 assert e.read() == ""
121 for inst in _active[:]:
122 inst.wait()
123 assert not _active
124 print "All OK"
126 if __name__ == '__main__':
127 _test()