popen2.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. """Spawn a command with pipes to its stdin, stdout, and optionally stderr.
  2. The normal os.popen(cmd, mode) call spawns a shell command and provides a
  3. file interface to just the input or output of the process depending on
  4. whether mode is 'r' or 'w'. This module provides the functions popen2(cmd)
  5. and popen3(cmd) which return two or three pipes to the spawned command.
  6. """
  7. import os
  8. import sys
  9. __all__ = ["popen2", "popen3", "popen4"]
  10. try:
  11. MAXFD = os.sysconf('SC_OPEN_MAX')
  12. except (AttributeError, ValueError):
  13. MAXFD = 256
  14. _active = []
  15. def _cleanup():
  16. for inst in _active[:]:
  17. inst.poll()
  18. class Popen3:
  19. """Class representing a child process. Normally instances are created
  20. by the factory functions popen2() and popen3()."""
  21. sts = -1 # Child not completed yet
  22. def __init__(self, cmd, capturestderr=False, bufsize=-1):
  23. """The parameter 'cmd' is the shell command to execute in a
  24. sub-process. On UNIX, 'cmd' may be a sequence, in which case arguments
  25. will be passed directly to the program without shell intervention (as
  26. with os.spawnv()). If 'cmd' is a string it will be passed to the shell
  27. (as with os.system()). The 'capturestderr' flag, if true, specifies
  28. that the object should capture standard error output of the child
  29. process. The default is false. If the 'bufsize' parameter is
  30. specified, it specifies the size of the I/O buffers to/from the child
  31. process."""
  32. _cleanup()
  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.dup2(p2cread, 0)
  41. os.dup2(c2pwrite, 1)
  42. if capturestderr:
  43. os.dup2(errin, 2)
  44. self._run_child(cmd)
  45. os.close(p2cread)
  46. self.tochild = os.fdopen(p2cwrite, 'w', bufsize)
  47. os.close(c2pwrite)
  48. self.fromchild = os.fdopen(c2pread, 'r', bufsize)
  49. if capturestderr:
  50. os.close(errin)
  51. self.childerr = os.fdopen(errout, 'r', bufsize)
  52. else:
  53. self.childerr = None
  54. _active.append(self)
  55. def _run_child(self, cmd):
  56. if isinstance(cmd, basestring):
  57. cmd = ['/bin/sh', '-c', cmd]
  58. for i in xrange(3, MAXFD):
  59. try:
  60. os.close(i)
  61. except OSError:
  62. pass
  63. try:
  64. os.execvp(cmd[0], cmd)
  65. finally:
  66. os._exit(1)
  67. def poll(self):
  68. """Return the exit status of the child process if it has finished,
  69. or -1 if it hasn't finished yet."""
  70. if self.sts < 0:
  71. try:
  72. pid, sts = os.waitpid(self.pid, os.WNOHANG)
  73. if pid == self.pid:
  74. self.sts = sts
  75. _active.remove(self)
  76. except os.error:
  77. pass
  78. return self.sts
  79. def wait(self):
  80. """Wait for and return the exit status of the child process."""
  81. if self.sts < 0:
  82. pid, sts = os.waitpid(self.pid, 0)
  83. if pid == self.pid:
  84. self.sts = sts
  85. _active.remove(self)
  86. return self.sts
  87. class Popen4(Popen3):
  88. childerr = None
  89. def __init__(self, cmd, bufsize=-1):
  90. _cleanup()
  91. p2cread, p2cwrite = os.pipe()
  92. c2pread, c2pwrite = os.pipe()
  93. self.pid = os.fork()
  94. if self.pid == 0:
  95. # Child
  96. os.dup2(p2cread, 0)
  97. os.dup2(c2pwrite, 1)
  98. os.dup2(c2pwrite, 2)
  99. self._run_child(cmd)
  100. os.close(p2cread)
  101. self.tochild = os.fdopen(p2cwrite, 'w', bufsize)
  102. os.close(c2pwrite)
  103. self.fromchild = os.fdopen(c2pread, 'r', bufsize)
  104. _active.append(self)
  105. if sys.platform[:3] == "win" or sys.platform == "os2emx":
  106. # Some things don't make sense on non-Unix platforms.
  107. del Popen3, Popen4
  108. def popen2(cmd, bufsize=-1, mode='t'):
  109. """Execute the shell command 'cmd' in a sub-process. On UNIX, 'cmd' may
  110. be a sequence, in which case arguments will be passed directly to the
  111. program without shell intervention (as with os.spawnv()). If 'cmd' is a
  112. string it will be passed to the shell (as with os.system()). If
  113. 'bufsize' is specified, it sets the buffer size for the I/O pipes. The
  114. file objects (child_stdout, child_stdin) are returned."""
  115. w, r = os.popen2(cmd, mode, bufsize)
  116. return r, w
  117. def popen3(cmd, bufsize=-1, mode='t'):
  118. """Execute the shell command 'cmd' in a sub-process. On UNIX, 'cmd' may
  119. be a sequence, in which case arguments will be passed directly to the
  120. program without shell intervention (as with os.spawnv()). If 'cmd' is a
  121. string it will be passed to the shell (as with os.system()). If
  122. 'bufsize' is specified, it sets the buffer size for the I/O pipes. The
  123. file objects (child_stdout, child_stdin, child_stderr) are returned."""
  124. w, r, e = os.popen3(cmd, mode, bufsize)
  125. return r, w, e
  126. def popen4(cmd, bufsize=-1, mode='t'):
  127. """Execute the shell command 'cmd' in a sub-process. On UNIX, 'cmd' may
  128. be a sequence, in which case arguments will be passed directly to the
  129. program without shell intervention (as with os.spawnv()). If 'cmd' is a
  130. string it will be passed to the shell (as with os.system()). If
  131. 'bufsize' is specified, it sets the buffer size for the I/O pipes. The
  132. file objects (child_stdout_stderr, child_stdin) are returned."""
  133. w, r = os.popen4(cmd, mode, bufsize)
  134. return r, w
  135. else:
  136. def popen2(cmd, bufsize=-1, mode='t'):
  137. """Execute the shell command 'cmd' in a sub-process. On UNIX, 'cmd' may
  138. be a sequence, in which case arguments will be passed directly to the
  139. program without shell intervention (as with os.spawnv()). If 'cmd' is a
  140. string it will be passed to the shell (as with os.system()). If
  141. 'bufsize' is specified, it sets the buffer size for the I/O pipes. The
  142. file objects (child_stdout, child_stdin) are returned."""
  143. inst = Popen3(cmd, False, bufsize)
  144. return inst.fromchild, inst.tochild
  145. def popen3(cmd, bufsize=-1, mode='t'):
  146. """Execute the shell command 'cmd' in a sub-process. On UNIX, 'cmd' may
  147. be a sequence, in which case arguments will be passed directly to the
  148. program without shell intervention (as with os.spawnv()). If 'cmd' is a
  149. string it will be passed to the shell (as with os.system()). If
  150. 'bufsize' is specified, it sets the buffer size for the I/O pipes. The
  151. file objects (child_stdout, child_stdin, child_stderr) are returned."""
  152. inst = Popen3(cmd, True, bufsize)
  153. return inst.fromchild, inst.tochild, inst.childerr
  154. def popen4(cmd, bufsize=-1, mode='t'):
  155. """Execute the shell command 'cmd' in a sub-process. On UNIX, 'cmd' may
  156. be a sequence, in which case arguments will be passed directly to the
  157. program without shell intervention (as with os.spawnv()). If 'cmd' is a
  158. string it will be passed to the shell (as with os.system()). If
  159. 'bufsize' is specified, it sets the buffer size for the I/O pipes. The
  160. file objects (child_stdout_stderr, child_stdin) are returned."""
  161. inst = Popen4(cmd, bufsize)
  162. return inst.fromchild, inst.tochild
  163. __all__.extend(["Popen3", "Popen4"])
  164. def _test():
  165. cmd = "cat"
  166. teststr = "ab cd\n"
  167. if os.name == "nt":
  168. cmd = "more"
  169. # "more" doesn't act the same way across Windows flavors,
  170. # sometimes adding an extra newline at the start or the
  171. # end. So we strip whitespace off both ends for comparison.
  172. expected = teststr.strip()
  173. print "testing popen2..."
  174. r, w = popen2(cmd)
  175. w.write(teststr)
  176. w.close()
  177. got = r.read()
  178. if got.strip() != expected:
  179. raise ValueError("wrote %r read %r" % (teststr, got))
  180. print "testing popen3..."
  181. try:
  182. r, w, e = popen3([cmd])
  183. except:
  184. r, w, e = popen3(cmd)
  185. w.write(teststr)
  186. w.close()
  187. got = r.read()
  188. if got.strip() != expected:
  189. raise ValueError("wrote %r read %r" % (teststr, got))
  190. got = e.read()
  191. if got:
  192. raise ValueError("unexpected %r on stderr" % (got,))
  193. for inst in _active[:]:
  194. inst.wait()
  195. if _active:
  196. raise ValueError("_active not empty")
  197. print "All OK"
  198. if __name__ == '__main__':
  199. _test()