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