diff options
| author | jquast <contact@jeffquast.com> | 2014-05-25 09:20:33 -0700 |
|---|---|---|
| committer | jquast <contact@jeffquast.com> | 2014-05-25 09:20:33 -0700 |
| commit | e4d63327d2d4890b1ef549f87f0a137e6fa7eb94 (patch) | |
| tree | bccb3f0614ea282a63e5d57886d02ea95d961ab7 | |
| parent | 78b16ad8f06ce20b5274a7a7c17f3a8a042edd46 (diff) | |
| download | pexpect-issue-20-try-2.tar.gz | |
more naughty attempts, cleaning up, as not to loseissue-20-try-2
| -rw-r--r-- | pexpect/__init__.py | 113 | ||||
| -rwxr-xr-x | tests/test_constructor.py | 6 | ||||
| -rwxr-xr-x | tests/test_ctrl_chars.py | 7 | ||||
| -rwxr-xr-x | tests/test_expect.py | 1 |
4 files changed, 83 insertions, 44 deletions
diff --git a/pexpect/__init__.py b/pexpect/__init__.py index bdae9a0..f7889fd 100644 --- a/pexpect/__init__.py +++ b/pexpect/__init__.py @@ -92,8 +92,15 @@ __revision__ = '' __all__ = ['ExceptionPexpect', 'EOF', 'TIMEOUT', 'spawn', 'spawnu', 'run', 'runu', 'which', 'split_command_line', '__version__', '__revision__'] +import inspect +from pprint import pprint + PY3 = (sys.version_info[0] >= 3) +if not PY3: + InterruptedError = select.error + + # Exception classes used by this module. class ExceptionPexpect(Exception): '''Base class for all exceptions raised by this module. @@ -457,7 +464,7 @@ class spawn(object): self.delaybeforesend = 0.05 # Used by close() to give kernel time to update process status. # Time in seconds. - self.delayafterclose = 0.1 + self.delayafterclose = 0.01 # Used by terminate() to give kernel time to update process status. # Time in seconds. self.delayafterterminate = 0.1 @@ -508,13 +515,15 @@ class spawn(object): then this does not close it. ''' if not self.closed: - # It is possible for __del__ methods to execute during the - # teardown of the Python VM itself. Thus self.close() may - # trigger an exception because os.close may be None. try: - self.close() - # which exception, shouldnt' we catch explicitly .. ? - except: + self.close(force=True) + except ExceptionPexpect: + # child process was not able to exit + pass + except Exception: + # It is possible for __del__ methods to execute during the + # teardown of the Python VM itself. Thus self.close() may + # trigger an exception because os.close may be None. pass def __str__(self): @@ -548,6 +557,8 @@ class spawn(object): s.append('delaybeforesend: ' + str(self.delaybeforesend)) s.append('delayafterclose: ' + str(self.delayafterclose)) s.append('delayafterterminate: ' + str(self.delayafterterminate)) + s.append('select: ' + repr(self.__select([self.child_fd], [], [], 0))) + s.append('isalive: ' + str(self.isalive())) return '\n'.join(s) def _spawn(self, command, args=[]): @@ -736,18 +747,19 @@ class spawn(object): behavior with files. Set force to True if you want to make sure that the child is terminated (SIGKILL is sent if the child ignores SIGHUP and SIGINT). ''' - if not self.closed: self.flush() os.close(self.child_fd) # Give kernel time to update process status. - time.sleep(self.delayafterclose) + #pprint('--start') + #pprint(inspect.stack()) + #pprint('--end') + select_sleep(self.delayafterclose) if self.isalive(): if not self.terminate(force): raise ExceptionPexpect('Could not terminate the child.') self.child_fd = -1 self.closed = True - #self.pid = None def flush(self): '''This does nothing. It is here to support the interface for a @@ -789,7 +801,7 @@ class spawn(object): return False if timeout is not None: timeout = end_time - time.time() - time.sleep(0.1) + select_sleep(0.1) def getecho(self): '''This returns the terminal echo mode. This returns True if echo is @@ -890,6 +902,7 @@ class spawn(object): # from the child_fd -- it will block forever or until TIMEOUT. # For this case, I test isalive() before doing any reading. # If isalive() is false, then I pretend that this is the same as EOF. + r = [] if not self.isalive(): # timeout of 0 means "poll" r, w, e = self.__select([self.child_fd], [], [], 0) @@ -906,25 +919,19 @@ class spawn(object): raise EOF('End Of File (EOF). Slow platform.') stime = time.time() - death = False - while True: + eof_flag = False + while not r: r, w, e = self.__select([self.child_fd], [], [], poll_exit) - elapsed = time.time() - stime - if r: - break - elif not death and not self.isalive(): - # Under Irix and OSX, even after all output from child_fd has - # been received, the parent process has not yet received a - # (pid, status) from waitpid(2) until at least one additional - # call to select(2) is issued. Therefor, we poll every - # `poll_exit` interval for waitpid() which may cause EOF. - death = True - # poll at least just one more time for output - poll_exit = 0 - elif death: - self.flag_eof = True + if not r and eof_flag: raise EOF('End of File (EOF). Very slow platform.') - elif timeout is not None: + # reproduced on travis-ci for python 3.2; select() returns + # nothing for reading, then isalive() returns False, more + # data remains for reading, so check one more time! + elif not r and not self.isalive(): + poll_exit = 0 + eof_flag = True + elif not r and timeout is not None: + elapsed = time.time() - stime if elapsed > timeout: raise TIMEOUT('Timeout exceeded.') else: @@ -1041,7 +1048,7 @@ class spawn(object): bytes written. If a logfile is specified, a copy is written to that log. ''' - time.sleep(self.delaybeforesend) + select_sleep(self.delaybeforesend) s = self._coerce_send_string(s) self._log(s, 'send') @@ -1150,20 +1157,20 @@ class spawn(object): return True try: self.kill(signal.SIGHUP) - time.sleep(self.delayafterterminate) + select_sleep(self.delayafterterminate) if not self.isalive(): return True self.kill(signal.SIGCONT) - time.sleep(self.delayafterterminate) + select_sleep(self.delayafterterminate) if not self.isalive(): return True self.kill(signal.SIGINT) - time.sleep(self.delayafterterminate) + select_sleep(self.delayafterterminate) if not self.isalive(): return True if force: self.kill(signal.SIGKILL) - time.sleep(self.delayafterterminate) + select_sleep(self.delayafterterminate) if not self.isalive(): return True else: @@ -1174,7 +1181,7 @@ class spawn(object): # this to happen. I think isalive() reports True, but the # process is dead to the kernel. # Make one last attempt to see if the kernel is up to date. - time.sleep(self.delayafterterminate) + select_sleep(self.delayafterterminate) if not self.isalive(): return True else: @@ -1521,10 +1528,10 @@ class spawn(object): if (timeout is not None) and (timeout < 0): raise TIMEOUT('Timeout exceeded in expect_any().') # Still have time left, so read more data - c = self.read_nonblocking(self.maxread, timeout) - freshlen = len(c) - time.sleep(0.0001) - incoming = incoming + c + inp_data = self.read_nonblocking(self.maxread, timeout) + freshlen = len(inp_data) + #select_sleep(0.0001) + incoming += inp_data if timeout is not None: timeout = end_time - time.time() except EOF: @@ -2063,4 +2070,34 @@ def split_command_line(command_line): arg_list.append(arg) return arg_list + +def select_sleep(timeout=None): + """select_sleep([timeout]) -> None + + timeout is specified in seconds; it may be a floating point number to + specify fractions of seconds. If it is absent or None, the call will + never time out. + """ + # it is necessary on some systems to avoid using time.sleep and its + # accompanying SIGARLM, it has a detrimental delay of signal propagation + # of at least SIGCHLD. There is a very simple solution; select() has great + # time resolution and avoids using signals all together. + import time + time.sleep(timeout) + #stime = time.time() + #while True: + # try: + # select.select([], [], [], timeout) + # # So what happens when a signal arrives and is handled (perhaps, by a + # # user-installed signal handler?), yet we requested to sleep for say, + # # 10 seconds, and 9 more seconds remain? Do the math resume for the + # # time remaining. + # except InterruptedError: + # if timeout is None: + # timeout -= time.time() - stime + # if timeout <= 0: + # break + # else: + # break + # vi:set sr et ts=4 sw=4 ft=python : diff --git a/tests/test_constructor.py b/tests/test_constructor.py index 8a98c28..a7a427f 100755 --- a/tests/test_constructor.py +++ b/tests/test_constructor.py @@ -29,11 +29,11 @@ class TestCaseConstructor(PexpectTestCase.PexpectTestCase): the same results for different styles of invoking __init__(). This assumes that the root directory / is static during the test. ''' - p1 = pexpect.spawn('/bin/ls -l /bin') - p2 = pexpect.spawn('/bin/ls' ,['-l', '/bin']) + p1 = pexpect.spawn('/bin/ls -i /bin') + p2 = pexpect.spawn('/bin/ls' ,['-i', '/bin']) p1.expect (pexpect.EOF) p2.expect (pexpect.EOF) - assert (p1.before == p2.before) + assert (p1.before == p2.before), (p1.before, p2.before) def test_named_parameters (self): '''This tests that named parameters work. diff --git a/tests/test_ctrl_chars.py b/tests/test_ctrl_chars.py index 336337c..9def111 100755 --- a/tests/test_ctrl_chars.py +++ b/tests/test_ctrl_chars.py @@ -39,12 +39,15 @@ class TestCtrlChars(PexpectTestCase.PexpectTestCase): process.''' child = pexpect.spawn('python getch.py') child.expect('READY', timeout=5) + i = None try: - for i in range(1,256): + for i in range(1, 256): child.send(byte(i)) - child.expect ('%d\r\n' % (i,)) # This needs to be last, as getch.py exits on \x00 child.send(byte(0)) + + for i in range(1, 256): + child.expect('%d\r\n' % (i,)) child.expect('0\r\n') child.expect(pexpect.EOF) except Exception: diff --git a/tests/test_expect.py b/tests/test_expect.py index bcaed82..d5d0c9c 100755 --- a/tests/test_expect.py +++ b/tests/test_expect.py @@ -460,7 +460,6 @@ class ExpectTestCase (PexpectTestCase.PexpectTestCase): self.assertEqual(expect([b'1, 2, 3', b'2,']), 1) def test_greed(self): - # fails on travis on occasion .. ? p = pexpect.spawn(self.PYTHONBIN + ' list100.py') self._greed(p.expect) |
