popen_spawn.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. """Provides an interface like pexpect.spawn interface using subprocess.Popen
  2. """
  3. import os
  4. import threading
  5. import subprocess
  6. import sys
  7. import time
  8. import signal
  9. import shlex
  10. try:
  11. from queue import Queue, Empty # Python 3
  12. except ImportError:
  13. from Queue import Queue, Empty # Python 2
  14. from .spawnbase import SpawnBase, PY3
  15. from .exceptions import EOF
  16. class PopenSpawn(SpawnBase):
  17. if PY3:
  18. crlf = '\n'.encode('ascii')
  19. else:
  20. crlf = '\n'
  21. def __init__(self, cmd, timeout=30, maxread=2000, searchwindowsize=None,
  22. logfile=None, cwd=None, env=None, encoding=None,
  23. codec_errors='strict'):
  24. super(PopenSpawn, self).__init__(timeout=timeout, maxread=maxread,
  25. searchwindowsize=searchwindowsize, logfile=logfile,
  26. encoding=encoding, codec_errors=codec_errors)
  27. kwargs = dict(bufsize=0, stdin=subprocess.PIPE,
  28. stderr=subprocess.STDOUT, stdout=subprocess.PIPE,
  29. cwd=cwd, env=env)
  30. if sys.platform == 'win32':
  31. startupinfo = subprocess.STARTUPINFO()
  32. startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
  33. kwargs['startupinfo'] = startupinfo
  34. kwargs['creationflags'] = subprocess.CREATE_NEW_PROCESS_GROUP
  35. if not isinstance(cmd, (list, tuple)):
  36. cmd = shlex.split(cmd)
  37. self.proc = subprocess.Popen(cmd, **kwargs)
  38. self.closed = False
  39. self._buf = self.string_type()
  40. self._read_queue = Queue()
  41. self._read_thread = threading.Thread(target=self._read_incoming)
  42. self._read_thread.setDaemon(True)
  43. self._read_thread.start()
  44. _read_reached_eof = False
  45. def read_nonblocking(self, size, timeout):
  46. buf = self._buf
  47. if self._read_reached_eof:
  48. # We have already finished reading. Use up any buffered data,
  49. # then raise EOF
  50. if buf:
  51. self._buf = buf[size:]
  52. return buf[:size]
  53. else:
  54. self.flag_eof = True
  55. raise EOF('End Of File (EOF).')
  56. if timeout == -1:
  57. timeout = self.timeout
  58. elif timeout is None:
  59. timeout = 1e6
  60. t0 = time.time()
  61. while (time.time() - t0) < timeout and size and len(buf) < size:
  62. try:
  63. incoming = self._read_queue.get_nowait()
  64. except Empty:
  65. break
  66. else:
  67. if incoming is None:
  68. self._read_reached_eof = True
  69. break
  70. buf += self._decoder.decode(incoming, final=False)
  71. r, self._buf = buf[:size], buf[size:]
  72. self._log(r, 'read')
  73. return r
  74. def _read_incoming(self):
  75. """Run in a thread to move output from a pipe to a queue."""
  76. fileno = self.proc.stdout.fileno()
  77. while 1:
  78. buf = b''
  79. try:
  80. buf = os.read(fileno, 1024)
  81. except OSError as e:
  82. self._log(e, 'read')
  83. if not buf:
  84. # This indicates we have reached EOF
  85. self._read_queue.put(None)
  86. return
  87. self._read_queue.put(buf)
  88. def write(self, s):
  89. '''This is similar to send() except that there is no return value.
  90. '''
  91. self.send(s)
  92. def writelines(self, sequence):
  93. '''This calls write() for each element in the sequence.
  94. The sequence can be any iterable object producing strings, typically a
  95. list of strings. This does not add line separators. There is no return
  96. value.
  97. '''
  98. for s in sequence:
  99. self.send(s)
  100. def send(self, s):
  101. '''Send data to the subprocess' stdin.
  102. Returns the number of bytes written.
  103. '''
  104. s = self._coerce_send_string(s)
  105. self._log(s, 'send')
  106. b = self._encoder.encode(s, final=False)
  107. if PY3:
  108. return self.proc.stdin.write(b)
  109. else:
  110. # On Python 2, .write() returns None, so we return the length of
  111. # bytes written ourselves. This assumes they all got written.
  112. self.proc.stdin.write(b)
  113. return len(b)
  114. def sendline(self, s=''):
  115. '''Wraps send(), sending string ``s`` to child process, with os.linesep
  116. automatically appended. Returns number of bytes written. '''
  117. n = self.send(s)
  118. return n + self.send(self.linesep)
  119. def wait(self):
  120. '''Wait for the subprocess to finish.
  121. Returns the exit code.
  122. '''
  123. status = self.proc.wait()
  124. if status >= 0:
  125. self.exitstatus = status
  126. self.signalstatus = None
  127. else:
  128. self.exitstatus = None
  129. self.signalstatus = -status
  130. self.terminated = True
  131. return status
  132. def kill(self, sig):
  133. '''Sends a Unix signal to the subprocess.
  134. Use constants from the :mod:`signal` module to specify which signal.
  135. '''
  136. if sys.platform == 'win32':
  137. if sig in [signal.SIGINT, signal.CTRL_C_EVENT]:
  138. sig = signal.CTRL_C_EVENT
  139. elif sig in [signal.SIGBREAK, signal.CTRL_BREAK_EVENT]:
  140. sig = signal.CTRL_BREAK_EVENT
  141. else:
  142. sig = signal.SIGTERM
  143. os.kill(self.proc.pid, sig)
  144. def sendeof(self):
  145. '''Closes the stdin pipe from the writing end.'''
  146. self.proc.stdin.close()