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
11import warnings
12warnings.warn("The popen2 module is deprecated.  Use the subprocess module.",
13              DeprecationWarning, stacklevel=2)
14
15__all__ = ["popen2", "popen3", "popen4"]
16
17try:
18    MAXFD = os.sysconf('SC_OPEN_MAX')
19except (AttributeError, ValueError):
20    MAXFD = 256
21
22_active = []
23
24def _cleanup():
25    for inst in _active[:]:
26        if inst.poll(_deadstate=sys.maxint) >= 0:
27            try:
28                _active.remove(inst)
29            except ValueError:
30                # This can happen if two threads create a new Popen instance.
31                # It's harmless that it was already removed, so ignore.
32                pass
33
34class Popen3:
35    """Class representing a child process.  Normally, instances are created
36    internally by the functions popen2() and popen3()."""
37
38    sts = -1                    # Child not completed yet
39
40    def __init__(self, cmd, capturestderr=False, bufsize=-1):
41        """The parameter 'cmd' is the shell command to execute in a
42        sub-process.  On UNIX, 'cmd' may be a sequence, in which case arguments
43        will be passed directly to the program without shell intervention (as
44        with os.spawnv()).  If 'cmd' is a string it will be passed to the shell
45        (as with os.system()).   The 'capturestderr' flag, if true, specifies
46        that the object should capture standard error output of the child
47        process.  The default is false.  If the 'bufsize' parameter is
48        specified, it specifies the size of the I/O buffers to/from the child
49        process."""
50        _cleanup()
51        self.cmd = cmd
52        p2cread, p2cwrite = os.pipe()
53        c2pread, c2pwrite = os.pipe()
54        if capturestderr:
55            errout, errin = os.pipe()
56        self.pid = os.fork()
57        if self.pid == 0:
58            # Child
59            os.dup2(p2cread, 0)
60            os.dup2(c2pwrite, 1)
61            if capturestderr:
62                os.dup2(errin, 2)
63            self._run_child(cmd)
64        os.close(p2cread)
65        self.tochild = os.fdopen(p2cwrite, 'w', bufsize)
66        os.close(c2pwrite)
67        self.fromchild = os.fdopen(c2pread, 'r', bufsize)
68        if capturestderr:
69            os.close(errin)
70            self.childerr = os.fdopen(errout, 'r', bufsize)
71        else:
72            self.childerr = None
73
74    def __del__(self):
75        # In case the child hasn't been waited on, check if it's done.
76        self.poll(_deadstate=sys.maxint)
77        if self.sts < 0:
78            if _active is not None:
79                # Child is still running, keep us alive until we can wait on it.
80                _active.append(self)
81
82    def _run_child(self, cmd):
83        if isinstance(cmd, basestring):
84            cmd = ['/bin/sh', '-c', cmd]
85        os.closerange(3, MAXFD)
86        try:
87            os.execvp(cmd[0], cmd)
88        finally:
89            os._exit(1)
90
91    def poll(self, _deadstate=None):
92        """Return the exit status of the child process if it has finished,
93        or -1 if it hasn't finished yet."""
94        if self.sts < 0:
95            try:
96                pid, sts = os.waitpid(self.pid, os.WNOHANG)
97                # pid will be 0 if self.pid hasn't terminated
98                if pid == self.pid:
99                    self.sts = sts
100            except os.error:
101                if _deadstate is not None:
102                    self.sts = _deadstate
103        return self.sts
104
105    def wait(self):
106        """Wait for and return the exit status of the child process."""
107        if self.sts < 0:
108            pid, sts = os.waitpid(self.pid, 0)
109            # This used to be a test, but it is believed to be
110            # always true, so I changed it to an assertion - mvl
111            assert pid == self.pid
112            self.sts = sts
113        return self.sts
114
115
116class Popen4(Popen3):
117    childerr = None
118
119    def __init__(self, cmd, bufsize=-1):
120        _cleanup()
121        self.cmd = cmd
122        p2cread, p2cwrite = os.pipe()
123        c2pread, c2pwrite = os.pipe()
124        self.pid = os.fork()
125        if self.pid == 0:
126            # Child
127            os.dup2(p2cread, 0)
128            os.dup2(c2pwrite, 1)
129            os.dup2(c2pwrite, 2)
130            self._run_child(cmd)
131        os.close(p2cread)
132        self.tochild = os.fdopen(p2cwrite, 'w', bufsize)
133        os.close(c2pwrite)
134        self.fromchild = os.fdopen(c2pread, 'r', bufsize)
135
136
137if sys.platform[:3] == "win" or sys.platform == "os2emx":
138    # Some things don't make sense on non-Unix platforms.
139    del Popen3, Popen4
140
141    def popen2(cmd, bufsize=-1, mode='t'):
142        """Execute the shell command 'cmd' in a sub-process. On UNIX, 'cmd' may
143        be a sequence, in which case arguments will be passed directly to the
144        program without shell intervention (as with os.spawnv()). If 'cmd' is a
145        string it will be passed to the shell (as with os.system()). If
146        'bufsize' is specified, it sets the buffer size for the I/O pipes. The
147        file objects (child_stdout, child_stdin) are returned."""
148        w, r = os.popen2(cmd, mode, bufsize)
149        return r, w
150
151    def popen3(cmd, bufsize=-1, mode='t'):
152        """Execute the shell command 'cmd' in a sub-process. On UNIX, 'cmd' may
153        be a sequence, in which case arguments will be passed directly to the
154        program without shell intervention (as with os.spawnv()). If 'cmd' is a
155        string it will be passed to the shell (as with os.system()). If
156        'bufsize' is specified, it sets the buffer size for the I/O pipes. The
157        file objects (child_stdout, child_stdin, child_stderr) are returned."""
158        w, r, e = os.popen3(cmd, mode, bufsize)
159        return r, w, e
160
161    def popen4(cmd, bufsize=-1, mode='t'):
162        """Execute the shell command 'cmd' in a sub-process. On UNIX, 'cmd' may
163        be a sequence, in which case arguments will be passed directly to the
164        program without shell intervention (as with os.spawnv()). If 'cmd' is a
165        string it will be passed to the shell (as with os.system()). If
166        'bufsize' is specified, it sets the buffer size for the I/O pipes. The
167        file objects (child_stdout_stderr, child_stdin) are returned."""
168        w, r = os.popen4(cmd, mode, bufsize)
169        return r, w
170else:
171    def popen2(cmd, bufsize=-1, mode='t'):
172        """Execute the shell command 'cmd' in a sub-process. On UNIX, 'cmd' may
173        be a sequence, in which case arguments will be passed directly to the
174        program without shell intervention (as with os.spawnv()). If 'cmd' is a
175        string it will be passed to the shell (as with os.system()). If
176        'bufsize' is specified, it sets the buffer size for the I/O pipes. The
177        file objects (child_stdout, child_stdin) are returned."""
178        inst = Popen3(cmd, False, bufsize)
179        return inst.fromchild, inst.tochild
180
181    def popen3(cmd, bufsize=-1, mode='t'):
182        """Execute the shell command 'cmd' in a sub-process. On UNIX, 'cmd' may
183        be a sequence, in which case arguments will be passed directly to the
184        program without shell intervention (as with os.spawnv()). If 'cmd' is a
185        string it will be passed to the shell (as with os.system()). If
186        'bufsize' is specified, it sets the buffer size for the I/O pipes. The
187        file objects (child_stdout, child_stdin, child_stderr) are returned."""
188        inst = Popen3(cmd, True, bufsize)
189        return inst.fromchild, inst.tochild, inst.childerr
190
191    def popen4(cmd, bufsize=-1, mode='t'):
192        """Execute the shell command 'cmd' in a sub-process. On UNIX, 'cmd' may
193        be a sequence, in which case arguments will be passed directly to the
194        program without shell intervention (as with os.spawnv()). If 'cmd' is a
195        string it will be passed to the shell (as with os.system()). If
196        'bufsize' is specified, it sets the buffer size for the I/O pipes. The
197        file objects (child_stdout_stderr, child_stdin) are returned."""
198        inst = Popen4(cmd, bufsize)
199        return inst.fromchild, inst.tochild
200
201    __all__.extend(["Popen3", "Popen4"])
202