| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 |
- """Provides an interface like pexpect.spawn interface using subprocess.Popen
- """
- import os
- import threading
- import subprocess
- import sys
- import time
- import signal
- import shlex
- try:
- from queue import Queue, Empty # Python 3
- except ImportError:
- from Queue import Queue, Empty # Python 2
- from .spawnbase import SpawnBase, PY3
- from .exceptions import EOF
- class PopenSpawn(SpawnBase):
- if PY3:
- crlf = '\n'.encode('ascii')
- else:
- crlf = '\n'
- def __init__(self, cmd, timeout=30, maxread=2000, searchwindowsize=None,
- logfile=None, cwd=None, env=None, encoding=None,
- codec_errors='strict'):
- super(PopenSpawn, self).__init__(timeout=timeout, maxread=maxread,
- searchwindowsize=searchwindowsize, logfile=logfile,
- encoding=encoding, codec_errors=codec_errors)
- kwargs = dict(bufsize=0, stdin=subprocess.PIPE,
- stderr=subprocess.STDOUT, stdout=subprocess.PIPE,
- cwd=cwd, env=env)
- if sys.platform == 'win32':
- startupinfo = subprocess.STARTUPINFO()
- startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
- kwargs['startupinfo'] = startupinfo
- kwargs['creationflags'] = subprocess.CREATE_NEW_PROCESS_GROUP
- if not isinstance(cmd, (list, tuple)):
- cmd = shlex.split(cmd)
- self.proc = subprocess.Popen(cmd, **kwargs)
- self.closed = False
- self._buf = self.string_type()
- self._read_queue = Queue()
- self._read_thread = threading.Thread(target=self._read_incoming)
- self._read_thread.setDaemon(True)
- self._read_thread.start()
- _read_reached_eof = False
- def read_nonblocking(self, size, timeout):
- buf = self._buf
- if self._read_reached_eof:
- # We have already finished reading. Use up any buffered data,
- # then raise EOF
- if buf:
- self._buf = buf[size:]
- return buf[:size]
- else:
- self.flag_eof = True
- raise EOF('End Of File (EOF).')
- if timeout == -1:
- timeout = self.timeout
- elif timeout is None:
- timeout = 1e6
- t0 = time.time()
- while (time.time() - t0) < timeout and size and len(buf) < size:
- try:
- incoming = self._read_queue.get_nowait()
- except Empty:
- break
- else:
- if incoming is None:
- self._read_reached_eof = True
- break
- buf += self._decoder.decode(incoming, final=False)
- r, self._buf = buf[:size], buf[size:]
- self._log(r, 'read')
- return r
- def _read_incoming(self):
- """Run in a thread to move output from a pipe to a queue."""
- fileno = self.proc.stdout.fileno()
- while 1:
- buf = b''
- try:
- buf = os.read(fileno, 1024)
- except OSError as e:
- self._log(e, 'read')
- if not buf:
- # This indicates we have reached EOF
- self._read_queue.put(None)
- return
- self._read_queue.put(buf)
- def write(self, s):
- '''This is similar to send() except that there is no return value.
- '''
- self.send(s)
- def writelines(self, sequence):
- '''This calls write() for each element in the sequence.
- The sequence can be any iterable object producing strings, typically a
- list of strings. This does not add line separators. There is no return
- value.
- '''
- for s in sequence:
- self.send(s)
- def send(self, s):
- '''Send data to the subprocess' stdin.
-
- Returns the number of bytes written.
- '''
- s = self._coerce_send_string(s)
- self._log(s, 'send')
- b = self._encoder.encode(s, final=False)
- if PY3:
- return self.proc.stdin.write(b)
- else:
- # On Python 2, .write() returns None, so we return the length of
- # bytes written ourselves. This assumes they all got written.
- self.proc.stdin.write(b)
- return len(b)
- def sendline(self, s=''):
- '''Wraps send(), sending string ``s`` to child process, with os.linesep
- automatically appended. Returns number of bytes written. '''
- n = self.send(s)
- return n + self.send(self.linesep)
- def wait(self):
- '''Wait for the subprocess to finish.
-
- Returns the exit code.
- '''
- status = self.proc.wait()
- if status >= 0:
- self.exitstatus = status
- self.signalstatus = None
- else:
- self.exitstatus = None
- self.signalstatus = -status
- self.terminated = True
- return status
- def kill(self, sig):
- '''Sends a Unix signal to the subprocess.
-
- Use constants from the :mod:`signal` module to specify which signal.
- '''
- if sys.platform == 'win32':
- if sig in [signal.SIGINT, signal.CTRL_C_EVENT]:
- sig = signal.CTRL_C_EVENT
- elif sig in [signal.SIGBREAK, signal.CTRL_BREAK_EVENT]:
- sig = signal.CTRL_BREAK_EVENT
- else:
- sig = signal.SIGTERM
- os.kill(self.proc.pid, sig)
- def sendeof(self):
- '''Closes the stdin pipe from the writing end.'''
- self.proc.stdin.close()
|