Statistics
| Revision:

root / tags / v1_0_2_Build_904 / extensions / extScripting / scripts / jython / Lib / popen2.py @ 10724

History | View | Annotate | Download (6.63 KB)

1 5782 jmvivo
"""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
MAXFD = 256     # Max number of file descriptors (os.getdtablesize()???)
15
16
_active = []
17
18
def _cleanup():
19
    for inst in _active[:]:
20
        inst.poll()
21
22
class Popen3:
23
    """Class representing a child process.  Normally instances are created
24
    by the factory functions popen2() and popen3()."""
25
26
    sts = -1                    # Child not completed yet
27
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)
57
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)
70
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
83
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
91
92
93
class Popen4(Popen3):
94
    childerr = None
95
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)
112
113
114
if sys.platform[:3] == "win":
115
    # Some things don't make sense on non-Unix platforms.
116
    del Popen3, Popen4
117
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
124
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
131
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
145
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
152
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
159
160
    __all__.extend(["Popen3", "Popen4"])
161
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"
196
197
if __name__ == '__main__':
198
    _test()