summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteven Silvester <steven.silvester@ieee.org>2014-11-29 23:05:08 -0600
committerThomas Kluyver <takowl@gmail.com>2015-09-12 11:49:09 +0100
commit85bb31aaaa4bdb002c9c020e12150508546dd614 (patch)
treecdd735ea5b61eb7fbd8030053740a5020214f006
parent28310295b0eddba0e4c94a4bd31ae85336663568 (diff)
downloadpexpect-git-85bb31aaaa4bdb002c9c020e12150508546dd614.tar.gz
Enhanced popen_spawn and added tests
- Use just newline character for end of line - Use shlex to split cmd args - Improved non-blocking read to act more like ptyspawn - Fix send method and add sendline - Add kill method - Add several tests
-rw-r--r--pexpect/popen_spawn.py102
1 files changed, 80 insertions, 22 deletions
diff --git a/pexpect/popen_spawn.py b/pexpect/popen_spawn.py
index eeeb1ae..0a186fe 100644
--- a/pexpect/popen_spawn.py
+++ b/pexpect/popen_spawn.py
@@ -4,15 +4,24 @@ import os
import threading
import subprocess
import sys
+import time
+import signal
+import shlex
try:
- from queue import Queue # Python 3
+ from queue import Queue, Empty # Python 3
except ImportError:
- from Queue import Queue # Python 2
+ from Queue import Queue, Empty # Python 2
-from .spawnbase import SpawnBase, SpawnBaseUnicode
+from .spawnbase import SpawnBase, SpawnBaseUnicode, 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):
super(PopenSpawn, self).__init__(timeout=timeout, maxread=maxread,
@@ -28,7 +37,11 @@ class PopenSpawn(SpawnBase):
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._read_queue = Queue()
@@ -36,29 +49,55 @@ class PopenSpawn(SpawnBase):
self._read_thread.setDaemon(True)
self._read_thread.start()
- def read_nonblocking(self, n):
- orig = len(self._buf)
- while 1:
+ def read_nonblocking(self, size, timeout):
+ if self.closed:
+ raise ValueError('I/O operation on closed file.')
+ elif self.flag_eof:
+ self.closed = True
+ raise EOF('End Of File (EOF).')
+
+ if timeout == -1:
+ timeout = self.timeout
+ elif timeout is None:
+ timeout = 1e6
+
+ t0 = time.time()
+ buf = b''
+ while (time.time() - t0) < timeout and size and len(buf) < size:
try:
- self._buf += self._read_queue.get_nowait()
- except Queue.Empty:
- return
+ incoming = self._read_queue.get_nowait()
+ except Empty:
+ break
else:
- if len(self._buf) - orig >= n:
- return
+ if incoming is None:
+ self.flag_eof = True
+ raise EOF('End of File')
+
+ buf += incoming
+
+ if len(buf) > size:
+ self.buffer = buf[size:]
+ buf = buf[:size]
+
+ self._log(buf, 'read')
+ return buf
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 = os.read(self.proc.stdout.fileno(), 1024)
- self._read_queue.put(buf)
+ buf = ''
+ try:
+ buf = os.read(fileno, 1024)
+ except OSError as e:
+ self._log(e, 'read')
- def readline(self):
- while not '\n' in self._buf:
- self.read_nonblocking(1024)
- ind = self._buf.index('\n')
- ret, self._buf = self._buf[:ind], self._buf[ind:]
- return ret
+ if not buf:
+ self._read_queue.put(None)
+ return
+
+ self._read_queue.put(buf)
+ time.sleep(0.001)
def write(self, s):
'''This is similar to send() except that there is no return value.
@@ -79,11 +118,18 @@ class PopenSpawn(SpawnBase):
return self.proc.stdin.write(s)
def send(self, s):
+ s = self._coerce_send_string(s)
self._log(s, 'send')
- return self._send(s)
- def sendline(self, line):
- return self.send(line + '\n')
+ return self._send(s)
+
+ 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)
+ n = n + self.send(self.linesep)
+ return n
def wait(self):
status = self.proc.wait()
@@ -96,6 +142,18 @@ class PopenSpawn(SpawnBase):
self.terminated = True
return status
+ def kill(self, sig):
+ 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)
+
+
class PopenSpawnUnicode(SpawnBaseUnicode, PopenSpawn):
def _send(self, s):
super(PopenSpawnUnicode, self)._send(s.encode(self.encoding, self.errors))