From fa084459198f7e9f4a2f346ea8bdac2ca102bc87 Mon Sep 17 00:00:00 2001 From: dluyer <53582923+dluyer@users.noreply.github.com> Date: Thu, 1 Aug 2019 14:18:15 -0700 Subject: Fix missing data in 'before', especially after timeouts - Fix 'before' return on eof/timeout/error. - Fix behavior when searchwindowsize is increased between calls to expect_loop (existing code could discard required content). Note, this is less efficient, but it is correct. The prior changes improved benchmarks but broke correctness; I haven't benchmarked to see if this is better or worse than the original code, before buffer_type use was introduced. It would be possible to make this more efficient with further complexity. Breakage introduced: https://github.com/pexpect/pexpect/commit/fd7332f59e003a8ee658647f883d4b03018c5e5b#diff-244f4b51bfbbbcb072d1428a79b9f4f7 Prior partial fix: https://github.com/pexpect/pexpect/commit/5a2e63f3fbfb39458c832a65bf8db330e954b8f6#diff-244f4b51bfbbbcb072d1428a79b9f4f7 --- pexpect/expect.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/pexpect/expect.py b/pexpect/expect.py index db376d5..35cc231 100644 --- a/pexpect/expect.py +++ b/pexpect/expect.py @@ -32,19 +32,20 @@ class Expecter(object): spawn._buffer.write(window[searcher.end:]) spawn.before = spawn._before.getvalue()[0:-(len(window) - searcher.start)] spawn._before = spawn.buffer_type() - spawn.after = window[searcher.start: searcher.end] + spawn._before.write(window[searcher.end:]) + spawn.after = window[searcher.start:searcher.end] spawn.match = searcher.match spawn.match_index = index # Found a match return index elif self.searchwindowsize: spawn._buffer = spawn.buffer_type() - spawn._buffer.write(window) + spawn._buffer.write(window[-self.searchwindowsize:]) def eof(self, err=None): spawn = self.spawn - spawn.before = spawn.buffer + spawn.before = spawn._before.getvalue() spawn._buffer = spawn.buffer_type() spawn._before = spawn.buffer_type() spawn.after = EOF @@ -65,7 +66,7 @@ class Expecter(object): def timeout(self, err=None): spawn = self.spawn - spawn.before = spawn.buffer + spawn.before = spawn._before.getvalue() spawn.after = TIMEOUT index = self.searcher.timeout_index if index >= 0: @@ -83,7 +84,7 @@ class Expecter(object): def errored(self): spawn = self.spawn - spawn.before = spawn.buffer + spawn.before = spawn._before.getvalue() spawn.after = None spawn.match = None spawn.match_index = None @@ -96,7 +97,10 @@ class Expecter(object): end_time = time.time() + timeout try: - incoming = spawn.buffer + if hasattr(spawn, '_before'): + incoming = spawn._before.getvalue() + else: + incoming = spawn.buffer spawn._buffer = spawn.buffer_type() spawn._before = spawn.buffer_type() while True: -- cgit v1.2.1 From 1ed3ac8b2fd45e6cbab0734c9acc37f7f617fba3 Mon Sep 17 00:00:00 2001 From: dluyer <53582923+dluyer@users.noreply.github.com> Date: Fri, 2 Aug 2019 12:07:54 -0700 Subject: Update spawnbase.py --- pexpect/spawnbase.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pexpect/spawnbase.py b/pexpect/spawnbase.py index 63c0b42..59e9057 100644 --- a/pexpect/spawnbase.py +++ b/pexpect/spawnbase.py @@ -120,6 +120,9 @@ class SpawnBase(object): self.async_pw_transport = None # This is the read buffer. See maxread. self._buffer = self.buffer_type() + # The buffer may be trimmed for efficiency reasons. This is the + # untrimmed buffer, used to create the before attribute. + self._before = self.buffer_type() def _log(self, s, direction): if self.logfile is not None: -- cgit v1.2.1 From 2b07b633edc1dfc42de79d94adcfda1ca6a4dbbe Mon Sep 17 00:00:00 2001 From: dluyer <53582923+dluyer@users.noreply.github.com> Date: Fri, 2 Aug 2019 12:14:15 -0700 Subject: Update expect.py --- pexpect/expect.py | 80 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 63 insertions(+), 17 deletions(-) diff --git a/pexpect/expect.py b/pexpect/expect.py index 35cc231..2d36962 100644 --- a/pexpect/expect.py +++ b/pexpect/expect.py @@ -9,6 +9,9 @@ class Expecter(object): if searchwindowsize == -1: searchwindowsize = spawn.searchwindowsize self.searchwindowsize = searchwindowsize + self.lookback = None + if hasattr(searcher, 'longest_string'): + self.lookback = searcher.longest_string def new_data(self, data): spawn = self.spawn @@ -18,15 +21,58 @@ class Expecter(object): spawn._buffer.write(data) spawn._before.write(data) - # determine which chunk of data to search; if a windowsize is - # specified, this is the *new* data + the preceding bytes - if self.searchwindowsize: - spawn._buffer.seek(max(0, pos - self.searchwindowsize)) - window = spawn._buffer.read(self.searchwindowsize + len(data)) + if data is None: + # First call from a new call to expect_loop. + # self.searchwindowsize may have changed. + before_len = spawn._before.tell() + buf_len = spawn._buffer.tell() + freshlen = before_len + if before_len > buf_len: + if not self.searchwindowsize: + spawn._buffer = spawn.buffer_type() + window = spawn._before.getvalue() + spawn._buffer.write(spawn._before.getvalue()) + elif buf_len < self.searchwindowsize: + spawn._buffer = spawn.buffer_type() + spawn._before.seek(max(0, pos - self.searchwindowsize)) + window = spawn._before.read() + spawn._buffer.write(window) + else: + spawn._buffer.seek(max(0, buf_len - self.searchwindowsize)) + window = spawn._buffer.read() + else: + if self.searchwindowsize: + spawn._buffer.seek(max(0, buf_len - self.searchwindowsize)) + window = spawn._buffer.read() + else: + window = spawn._buffer.getvalue() else: - # otherwise, search the whole buffer (really slow for large datasets) - window = spawn.buffer - index = searcher.search(window, len(data)) + freshlen = len(data) + spawn._before.write(data) + if not self.searchwindowsize: + if self.lookback: + # search lookback + new data. + old_len = spawn._buffer.tell() + spawn._buffer.write(data) + spawn._buffer.seek(max(0, old_len - self.lookback)) + window = spawn._buffer.read() + else: + # copy the whole buffer (really slow for large datasets). + spawn._buffer.write(data) + window = spawn.buffer + else: + if len(data) >= self.searchwindowsize or not self._buffer.tell(): + window = data[-self.searchwindowsize:] + spawn._buffer = spawn.buffer_type() + spawn._buffer.write(window[-self.searchwindowsize:]) + else: + spawn._buffer.write(data) + new_len = self._buffer.tell() + spawn._buffer.seek(max(0, new_len - self.searchwindowsize)) + window = spawn._buffer.read() + if freshlen > len(window): + freshlen = len(window) + index = searcher.search(window, freshlen, self.searchwindowsize) if index >= 0: spawn._buffer = spawn.buffer_type() spawn._buffer.write(window[searcher.end:]) @@ -38,9 +84,11 @@ class Expecter(object): spawn.match_index = index # Found a match return index - elif self.searchwindowsize: - spawn._buffer = spawn.buffer_type() - spawn._buffer.write(window[-self.searchwindowsize:]) + elif self.searchwindowsize or self.lookback: + maintain = self.searchwindowsize or self.lookback + if spawn._buffer.tell() > maintain: + spawn._buffer = spawn.buffer_type() + spawn._buffer.write(window[-maintain:]) def eof(self, err=None): spawn = self.spawn @@ -97,12 +145,7 @@ class Expecter(object): end_time = time.time() + timeout try: - if hasattr(spawn, '_before'): - incoming = spawn._before.getvalue() - else: - incoming = spawn.buffer - spawn._buffer = spawn.buffer_type() - spawn._before = spawn.buffer_type() + incoming = None while True: idx = self.new_data(incoming) # Keep reading until exception or return. @@ -152,6 +195,7 @@ class searcher_string(object): self.eof_index = -1 self.timeout_index = -1 self._strings = [] + self.longest_string = 0 for n, s in enumerate(strings): if s is EOF: self.eof_index = n @@ -160,6 +204,8 @@ class searcher_string(object): self.timeout_index = n continue self._strings.append((n, s)) + if len(s) > self.longest_string: + self.longest_string = len(s) def __str__(self): '''This returns a human-readable string that represents the state of -- cgit v1.2.1 From 8e51b1fe81e509c2086e9855e7a81d4ac11d4101 Mon Sep 17 00:00:00 2001 From: dluyer <53582923+dluyer@users.noreply.github.com> Date: Fri, 2 Aug 2019 12:23:25 -0700 Subject: Update expect.py --- pexpect/expect.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pexpect/expect.py b/pexpect/expect.py index 2d36962..6ae196e 100644 --- a/pexpect/expect.py +++ b/pexpect/expect.py @@ -47,7 +47,7 @@ class Expecter(object): else: window = spawn._buffer.getvalue() else: - freshlen = len(data) + freshlen = len(data) spawn._before.write(data) if not self.searchwindowsize: if self.lookback: @@ -71,7 +71,7 @@ class Expecter(object): spawn._buffer.seek(max(0, new_len - self.searchwindowsize)) window = spawn._buffer.read() if freshlen > len(window): - freshlen = len(window) + freshlen = len(window) index = searcher.search(window, freshlen, self.searchwindowsize) if index >= 0: spawn._buffer = spawn.buffer_type() -- cgit v1.2.1 From ad3a8e90f1446283e7c0edef7065b9808963ee1b Mon Sep 17 00:00:00 2001 From: dluyer <53582923+dluyer@users.noreply.github.com> Date: Fri, 2 Aug 2019 13:06:42 -0700 Subject: Update expect.py --- pexpect/expect.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pexpect/expect.py b/pexpect/expect.py index 6ae196e..c437aa0 100644 --- a/pexpect/expect.py +++ b/pexpect/expect.py @@ -17,10 +17,6 @@ class Expecter(object): spawn = self.spawn searcher = self.searcher - pos = spawn._buffer.tell() - spawn._buffer.write(data) - spawn._before.write(data) - if data is None: # First call from a new call to expect_loop. # self.searchwindowsize may have changed. -- cgit v1.2.1 From 07a46a8c78caf4d6b6bd43134dfffcbf48663bb5 Mon Sep 17 00:00:00 2001 From: dluyer <53582923+dluyer@users.noreply.github.com> Date: Fri, 2 Aug 2019 13:17:14 -0700 Subject: Update expect.py --- pexpect/expect.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pexpect/expect.py b/pexpect/expect.py index c437aa0..28a224d 100644 --- a/pexpect/expect.py +++ b/pexpect/expect.py @@ -57,13 +57,13 @@ class Expecter(object): spawn._buffer.write(data) window = spawn.buffer else: - if len(data) >= self.searchwindowsize or not self._buffer.tell(): + if len(data) >= self.searchwindowsize or not spawn._buffer.tell(): window = data[-self.searchwindowsize:] spawn._buffer = spawn.buffer_type() spawn._buffer.write(window[-self.searchwindowsize:]) else: spawn._buffer.write(data) - new_len = self._buffer.tell() + new_len = spawn._buffer.tell() spawn._buffer.seek(max(0, new_len - self.searchwindowsize)) window = spawn._buffer.read() if freshlen > len(window): -- cgit v1.2.1 From 7ab86626d1e88a7f844865c598658121e2f68a42 Mon Sep 17 00:00:00 2001 From: dluyer <53582923+dluyer@users.noreply.github.com> Date: Fri, 2 Aug 2019 14:46:35 -0700 Subject: Updates from internal code review. --- pexpect/expect.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/pexpect/expect.py b/pexpect/expect.py index 28a224d..a9c3c49 100644 --- a/pexpect/expect.py +++ b/pexpect/expect.py @@ -6,6 +6,8 @@ class Expecter(object): def __init__(self, spawn, searcher, searchwindowsize=-1): self.spawn = spawn self.searcher = searcher + # A value of -1 means to use the figure from spawn, which should + # be None or a positive number. if searchwindowsize == -1: searchwindowsize = spawn.searchwindowsize self.searchwindowsize = searchwindowsize @@ -30,7 +32,8 @@ class Expecter(object): spawn._buffer.write(spawn._before.getvalue()) elif buf_len < self.searchwindowsize: spawn._buffer = spawn.buffer_type() - spawn._before.seek(max(0, pos - self.searchwindowsize)) + spawn._before.seek( + max(0, before_len - self.searchwindowsize)) window = spawn._before.read() spawn._buffer.write(window) else: @@ -66,13 +69,15 @@ class Expecter(object): new_len = spawn._buffer.tell() spawn._buffer.seek(max(0, new_len - self.searchwindowsize)) window = spawn._buffer.read() + if freshlen > len(window): freshlen = len(window) index = searcher.search(window, freshlen, self.searchwindowsize) if index >= 0: spawn._buffer = spawn.buffer_type() spawn._buffer.write(window[searcher.end:]) - spawn.before = spawn._before.getvalue()[0:-(len(window) - searcher.start)] + spawn.before = spawn._before.getvalue()[ + 0:-(len(window) - searcher.start)] spawn._before = spawn.buffer_type() spawn._before.write(window[searcher.end:]) spawn.after = window[searcher.start:searcher.end] -- cgit v1.2.1 From b5d6e6d56ff24027a98b2ed901e40dc69aa6761d Mon Sep 17 00:00:00 2001 From: dluyer <53582923+dluyer@users.noreply.github.com> Date: Thu, 9 Jan 2020 14:33:55 -0800 Subject: Update _async.py --- pexpect/_async.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pexpect/_async.py b/pexpect/_async.py index ca2044e..1ecbe81 100644 --- a/pexpect/_async.py +++ b/pexpect/_async.py @@ -8,10 +8,7 @@ from pexpect import EOF def expect_async(expecter, timeout=None): # First process data that was previously read - if it maches, we don't need # async stuff. - previously_read = expecter.spawn.buffer - expecter.spawn._buffer = expecter.spawn.buffer_type() - expecter.spawn._before = expecter.spawn.buffer_type() - idx = expecter.new_data(previously_read) + idx = expecter.existing_data() if idx is not None: return idx if not expecter.spawn.async_pw_transport: -- cgit v1.2.1 From 5f98ced27fcd4477776c2793e033794955b25bcd Mon Sep 17 00:00:00 2001 From: dluyer <53582923+dluyer@users.noreply.github.com> Date: Thu, 9 Jan 2020 14:42:30 -0800 Subject: Split new_data initial and subsequent calls expect_{loop,async} call new_data at start, when there isn't really new data. Reviewer requested a split of functionality here into two routines. --- pexpect/expect.py | 112 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 59 insertions(+), 53 deletions(-) diff --git a/pexpect/expect.py b/pexpect/expect.py index d6600a9..7d0c7ab 100644 --- a/pexpect/expect.py +++ b/pexpect/expect.py @@ -15,61 +15,9 @@ class Expecter(object): if hasattr(searcher, 'longest_string'): self.lookback = searcher.longest_string - def new_data(self, data): + def do_search(self, window, freshlen): spawn = self.spawn searcher = self.searcher - - if data is None: - # First call from a new call to expect_loop. - # self.searchwindowsize may have changed. - before_len = spawn._before.tell() - buf_len = spawn._buffer.tell() - freshlen = before_len - if before_len > buf_len: - if not self.searchwindowsize: - spawn._buffer = spawn.buffer_type() - window = spawn._before.getvalue() - spawn._buffer.write(spawn._before.getvalue()) - elif buf_len < self.searchwindowsize: - spawn._buffer = spawn.buffer_type() - spawn._before.seek( - max(0, before_len - self.searchwindowsize)) - window = spawn._before.read() - spawn._buffer.write(window) - else: - spawn._buffer.seek(max(0, buf_len - self.searchwindowsize)) - window = spawn._buffer.read() - else: - if self.searchwindowsize: - spawn._buffer.seek(max(0, buf_len - self.searchwindowsize)) - window = spawn._buffer.read() - else: - window = spawn._buffer.getvalue() - else: - freshlen = len(data) - spawn._before.write(data) - if not self.searchwindowsize: - if self.lookback: - # search lookback + new data. - old_len = spawn._buffer.tell() - spawn._buffer.write(data) - spawn._buffer.seek(max(0, old_len - self.lookback)) - window = spawn._buffer.read() - else: - # copy the whole buffer (really slow for large datasets). - spawn._buffer.write(data) - window = spawn.buffer - else: - if len(data) >= self.searchwindowsize or not spawn._buffer.tell(): - window = data[-self.searchwindowsize:] - spawn._buffer = spawn.buffer_type() - spawn._buffer.write(window[-self.searchwindowsize:]) - else: - spawn._buffer.write(data) - new_len = spawn._buffer.tell() - spawn._buffer.seek(max(0, new_len - self.searchwindowsize)) - window = spawn._buffer.read() - if freshlen > len(window): freshlen = len(window) index = searcher.search(window, freshlen, self.searchwindowsize) @@ -91,6 +39,64 @@ class Expecter(object): spawn._buffer = spawn.buffer_type() spawn._buffer.write(window[-maintain:]) + def existing_data(self): + # First call from a new call to expect_loop or expect_async. + # self.searchwindowsize may have changed. + # Treat all data as fresh. + spawn = self.spawn + before_len = spawn._before.tell() + buf_len = spawn._buffer.tell() + freshlen = before_len + if before_len > buf_len: + if not self.searchwindowsize: + spawn._buffer = spawn.buffer_type() + window = spawn._before.getvalue() + spawn._buffer.write(spawn._before.getvalue()) + elif buf_len < self.searchwindowsize: + spawn._buffer = spawn.buffer_type() + spawn._before.seek( + max(0, before_len - self.searchwindowsize)) + window = spawn._before.read() + spawn._buffer.write(window) + else: + spawn._buffer.seek(max(0, buf_len - self.searchwindowsize)) + window = spawn._buffer.read() + else: + if self.searchwindowsize: + spawn._buffer.seek(max(0, buf_len - self.searchwindowsize)) + window = spawn._buffer.read() + else: + window = spawn._buffer.getvalue() + return self.do_search(window, freshlen) + + def new_data(self, data): + # A subsequent call, after a call to existing_data. + spawn = self.spawn + freshlen = len(data) + spawn._before.write(data) + if not self.searchwindowsize: + if self.lookback: + # search lookback + new data. + old_len = spawn._buffer.tell() + spawn._buffer.write(data) + spawn._buffer.seek(max(0, old_len - self.lookback)) + window = spawn._buffer.read() + else: + # copy the whole buffer (really slow for large datasets). + spawn._buffer.write(data) + window = spawn.buffer + else: + if len(data) >= self.searchwindowsize or not spawn._buffer.tell(): + window = data[-self.searchwindowsize:] + spawn._buffer = spawn.buffer_type() + spawn._buffer.write(window[-self.searchwindowsize:]) + else: + spawn._buffer.write(data) + new_len = spawn._buffer.tell() + spawn._buffer.seek(max(0, new_len - self.searchwindowsize)) + window = spawn._buffer.read() + return self.do_search(window, freshlen) + def eof(self, err=None): spawn = self.spawn -- cgit v1.2.1 From c58e7e7d52a3f4471baf24748ffb0fb12526f686 Mon Sep 17 00:00:00 2001 From: dluyer <53582923+dluyer@users.noreply.github.com> Date: Thu, 9 Jan 2020 14:44:32 -0800 Subject: Updated expect_loop for split new_data --- pexpect/expect.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/pexpect/expect.py b/pexpect/expect.py index 7d0c7ab..572bd58 100644 --- a/pexpect/expect.py +++ b/pexpect/expect.py @@ -158,12 +158,10 @@ class Expecter(object): end_time = time.time() + timeout try: - incoming = None + idx = self.existing_data() + if idx is not None: + return idx while True: - idx = self.new_data(incoming) - # Keep reading until exception or return. - if idx is not None: - return idx # No match at this point if (timeout is not None) and (timeout < 0): return self.timeout() @@ -171,6 +169,10 @@ class Expecter(object): incoming = spawn.read_nonblocking(spawn.maxread, timeout) if self.spawn.delayafterread is not None: time.sleep(self.spawn.delayafterread) + idx = self.new_data(incoming) + # Keep reading until exception or return. + if idx is not None: + return idx if timeout is not None: timeout = end_time - time.time() except EOF as e: -- cgit v1.2.1 From 2bbb684cb399bbe203fe6a7103640d77b5c4a580 Mon Sep 17 00:00:00 2001 From: dluyer <53582923+dluyer@users.noreply.github.com> Date: Fri, 10 Jan 2020 11:45:35 -0800 Subject: Add test for truncation issue fixed in #579 --- tests/test_expect.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/test_expect.py b/tests/test_expect.py index c62231a..8a8c595 100755 --- a/tests/test_expect.py +++ b/tests/test_expect.py @@ -441,7 +441,7 @@ class ExpectTestCase (PexpectTestCase.PexpectTestCase): ''' p = pexpect.spawn('%s -Wi list100.py' % self.PYTHONBIN, env=no_coverage_env()) self._before_after(p) - + def test_before_after_exact(self): '''This tests some simple before/after things, for expect_exact(). (Grahn broke it at one point.) @@ -451,6 +451,22 @@ class ExpectTestCase (PexpectTestCase.PexpectTestCase): p.expect = p.expect_exact self._before_after(p) + def test_before_after_timeout(self): + child = pexpect.spawn('cat', echo=False) + child.sendline('BEGIN') + for i in range(100): + child.sendline('foo' * 100) + e = child.expect(['xyzzy', pexpect.TIMEOUT], + searchwindowsize=10, timeout=0.001) + self.assertEqual(e, 1) + child.sendline('xyzzy') + e = child.expect(['xyzzy', pexpect.TIMEOUT], + searchwindowsize=10, timeout=30) + self.assertEqual(e, 0) + self.assertEqual(child.before[0:5], 'BEGIN') + child.sendeof() + child.expect(pexpect.EOF) + def _ordering(self, p): p.timeout = 20 p.expect(b'>>> ') -- cgit v1.2.1 From 51c0fb2994d0c41a16a98e5f3b1ad3ced5264c58 Mon Sep 17 00:00:00 2001 From: dluyer <53582923+dluyer@users.noreply.github.com> Date: Fri, 10 Jan 2020 11:52:22 -0800 Subject: Whitespace cleanup --- tests/test_expect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_expect.py b/tests/test_expect.py index 8a8c595..cf118eb 100755 --- a/tests/test_expect.py +++ b/tests/test_expect.py @@ -441,7 +441,7 @@ class ExpectTestCase (PexpectTestCase.PexpectTestCase): ''' p = pexpect.spawn('%s -Wi list100.py' % self.PYTHONBIN, env=no_coverage_env()) self._before_after(p) - + def test_before_after_exact(self): '''This tests some simple before/after things, for expect_exact(). (Grahn broke it at one point.) -- cgit v1.2.1 From d8c01ed4092a96f5c2787f5a9a714811852a7dcd Mon Sep 17 00:00:00 2001 From: dluyer <53582923+dluyer@users.noreply.github.com> Date: Fri, 10 Jan 2020 11:53:46 -0800 Subject: Whitespace cleanup --- pexpect/expect.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pexpect/expect.py b/pexpect/expect.py index 572bd58..53b7e83 100644 --- a/pexpect/expect.py +++ b/pexpect/expect.py @@ -120,7 +120,7 @@ class Expecter(object): exc = EOF(msg) exc.__cause__ = None # in Python 3.x we can use "raise exc from None" raise exc - + def timeout(self, err=None): spawn = self.spawn @@ -149,7 +149,7 @@ class Expecter(object): spawn.after = None spawn.match = None spawn.match_index = None - + def expect_loop(self, timeout=-1): """Blocking expect""" spawn = self.spawn -- cgit v1.2.1 From 78a62d5b3253e88f2c7f4c8bae7d9aa9deabb575 Mon Sep 17 00:00:00 2001 From: dluyer <53582923+dluyer@users.noreply.github.com> Date: Fri, 10 Jan 2020 11:54:50 -0800 Subject: Whitespace cleanup --- pexpect/screen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pexpect/screen.py b/pexpect/screen.py index 5ab45b9..79f95c4 100644 --- a/pexpect/screen.py +++ b/pexpect/screen.py @@ -90,7 +90,7 @@ class screen: self.encoding = encoding self.encoding_errors = encoding_errors if encoding is not None: - self.decoder = codecs.getincrementaldecoder(encoding)(encoding_errors) + self.decoder = codecs.getincrementaldecoder(encoding)(encoding_errors) else: self.decoder = None self.cur_r = 1 -- cgit v1.2.1 From 5daa766b6d0d0efd7e4e51c72a0b5181e58d2e4c Mon Sep 17 00:00:00 2001 From: dluyer <53582923+dluyer@users.noreply.github.com> Date: Fri, 10 Jan 2020 11:55:59 -0800 Subject: Whitespace cleanup. --- pexpect/run.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pexpect/run.py b/pexpect/run.py index d9dfe76..af73137 100644 --- a/pexpect/run.py +++ b/pexpect/run.py @@ -49,7 +49,7 @@ def run(command, timeout=30, withexitstatus=False, events=None, (command_output, exitstatus) = run('ls -l /bin', withexitstatus=1) The following will run SSH and execute 'ls -l' on the remote machine. The - password 'secret' will be sent if the '(?i)password' pattern is ever seen:: + password 'secret' will be sent if the '(?i)password' pattern is ever seen: run("ssh username@machine.example.com 'ls -l'", events={'(?i)password':'secret\\n'}) @@ -67,7 +67,7 @@ def run(command, timeout=30, withexitstatus=False, events=None, contains patterns and responses. Whenever one of the patterns is seen in the command output, run() will send the associated response string. So, run() in the above example can be also written as: - + run("mencoder dvd://1 -o video.avi -oac copy -ovc copy", events=[(TIMEOUT,print_ticks)], timeout=5) -- cgit v1.2.1 From cf821ce4e3960fda579d8dad16ebb68f37a75128 Mon Sep 17 00:00:00 2001 From: dluyer <53582923+dluyer@users.noreply.github.com> Date: Fri, 10 Jan 2020 14:55:11 -0800 Subject: Fix test_before_after_timeout for PY3. --- tests/test_expect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_expect.py b/tests/test_expect.py index cf118eb..e4d023f 100755 --- a/tests/test_expect.py +++ b/tests/test_expect.py @@ -463,7 +463,7 @@ class ExpectTestCase (PexpectTestCase.PexpectTestCase): e = child.expect(['xyzzy', pexpect.TIMEOUT], searchwindowsize=10, timeout=30) self.assertEqual(e, 0) - self.assertEqual(child.before[0:5], 'BEGIN') + self.assertEqual(child.before[0:5], b'BEGIN') child.sendeof() child.expect(pexpect.EOF) -- cgit v1.2.1 From 5eab9f4b01d586420546448fd4838315062fdbad Mon Sep 17 00:00:00 2001 From: dluyer <53582923+dluyer@users.noreply.github.com> Date: Fri, 10 Jan 2020 15:20:34 -0800 Subject: Add test for increasing searchwindowsize --- tests/test_expect.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/tests/test_expect.py b/tests/test_expect.py index e4d023f..dc768a0 100755 --- a/tests/test_expect.py +++ b/tests/test_expect.py @@ -452,21 +452,40 @@ class ExpectTestCase (PexpectTestCase.PexpectTestCase): self._before_after(p) def test_before_after_timeout(self): + '''Tests that before is not truncated after a timeout, a bug in 4.7.''' child = pexpect.spawn('cat', echo=False) child.sendline('BEGIN') for i in range(100): child.sendline('foo' * 100) - e = child.expect(['xyzzy', pexpect.TIMEOUT], + e = child.expect([b'xyzzy', pexpect.TIMEOUT], searchwindowsize=10, timeout=0.001) self.assertEqual(e, 1) child.sendline('xyzzy') - e = child.expect(['xyzzy', pexpect.TIMEOUT], + e = child.expect([b'xyzzy', pexpect.TIMEOUT], searchwindowsize=10, timeout=30) self.assertEqual(e, 0) self.assertEqual(child.before[0:5], b'BEGIN') child.sendeof() child.expect(pexpect.EOF) + def test_increasing_searchwindowsize(self): + '''Tests that the search window can be expanded, a bug in 4.7.''' + child = pexpect.spawn('cat', echo=False) + child.sendline('BEGIN') + for i in range(100): + child.sendline('foo' * 100) + e = child.expect([b'xyzzy', pexpect.TIMEOUT], + searchwindowsize=10, timeout=0.5) + self.assertEqual(e, 1) + e = child.expect([b'BEGIN', pexpect.TIMEOUT], + searchwindowsize=10, timeout=0.5) + self.assertEqual(e, 1) + e = child.expect([b'BEGIN', pexpect.TIMEOUT], + searchwindowsize=40000, timeout=30.0) + self.assertEqual(e, 0) + child.sendeof() + child.expect(pexpect.EOF) + def _ordering(self, p): p.timeout = 20 p.expect(b'>>> ') -- cgit v1.2.1 From 80f9820c41e55ca6dbac0108c6d515a62af0093d Mon Sep 17 00:00:00 2001 From: dluyer <53582923+dluyer@users.noreply.github.com> Date: Fri, 10 Jan 2020 15:56:43 -0800 Subject: Add another regression test --- tests/test_expect.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/test_expect.py b/tests/test_expect.py index dc768a0..f16bf44 100755 --- a/tests/test_expect.py +++ b/tests/test_expect.py @@ -486,6 +486,12 @@ class ExpectTestCase (PexpectTestCase.PexpectTestCase): child.sendeof() child.expect(pexpect.EOF) + def test_searchwindowsize(self): + '''Tests that we don't match outside the window, a bug in 4.7.''' + p = pexpect.spawn('echo foobarbazbop') + e = p.expect([b'bar', b'bop'], searchwindowsize=6) + self.assertEqual(e, 1) + def _ordering(self, p): p.timeout = 20 p.expect(b'>>> ') -- cgit v1.2.1 From 3412f4d50b35b0e7b2cc382ab05e63f16bb2e02d Mon Sep 17 00:00:00 2001 From: dluyer <53582923+dluyer@users.noreply.github.com> Date: Fri, 10 Jan 2020 16:11:45 -0800 Subject: Provide bad version range in regression test docstrings --- tests/test_expect.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_expect.py b/tests/test_expect.py index f16bf44..c37e159 100755 --- a/tests/test_expect.py +++ b/tests/test_expect.py @@ -452,7 +452,7 @@ class ExpectTestCase (PexpectTestCase.PexpectTestCase): self._before_after(p) def test_before_after_timeout(self): - '''Tests that before is not truncated after a timeout, a bug in 4.7.''' + '''Tests that timeouts do not truncate before, a bug in 4.4-4.7.''' child = pexpect.spawn('cat', echo=False) child.sendline('BEGIN') for i in range(100): @@ -469,7 +469,7 @@ class ExpectTestCase (PexpectTestCase.PexpectTestCase): child.expect(pexpect.EOF) def test_increasing_searchwindowsize(self): - '''Tests that the search window can be expanded, a bug in 4.7.''' + '''Tests that the search window can be expanded, a bug in 4.4-4.7.''' child = pexpect.spawn('cat', echo=False) child.sendline('BEGIN') for i in range(100): @@ -487,7 +487,7 @@ class ExpectTestCase (PexpectTestCase.PexpectTestCase): child.expect(pexpect.EOF) def test_searchwindowsize(self): - '''Tests that we don't match outside the window, a bug in 4.7.''' + '''Tests that we don't match outside the window, a bug in 4.4-4.7.''' p = pexpect.spawn('echo foobarbazbop') e = p.expect([b'bar', b'bop'], searchwindowsize=6) self.assertEqual(e, 1) -- cgit v1.2.1 From 4228cde2fd26f321fba4aec9205537d9d1614817 Mon Sep 17 00:00:00 2001 From: dluyer <53582923+dluyer@users.noreply.github.com> Date: Tue, 14 Jan 2020 08:10:58 -0800 Subject: Undo incorrect formatting update --- pexpect/run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pexpect/run.py b/pexpect/run.py index af73137..ff288a1 100644 --- a/pexpect/run.py +++ b/pexpect/run.py @@ -49,7 +49,7 @@ def run(command, timeout=30, withexitstatus=False, events=None, (command_output, exitstatus) = run('ls -l /bin', withexitstatus=1) The following will run SSH and execute 'ls -l' on the remote machine. The - password 'secret' will be sent if the '(?i)password' pattern is ever seen: + password 'secret' will be sent if the '(?i)password' pattern is ever seen:: run("ssh username@machine.example.com 'ls -l'", events={'(?i)password':'secret\\n'}) -- cgit v1.2.1 From ad4be95a2f022c5154ca625638bd5040d99ef9cb Mon Sep 17 00:00:00 2001 From: dluyer <53582923+dluyer@users.noreply.github.com> Date: Tue, 14 Jan 2020 08:26:12 -0800 Subject: Avoid calling getvalue() twice in a row --- pexpect/expect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pexpect/expect.py b/pexpect/expect.py index 53b7e83..d3409db 100644 --- a/pexpect/expect.py +++ b/pexpect/expect.py @@ -51,7 +51,7 @@ class Expecter(object): if not self.searchwindowsize: spawn._buffer = spawn.buffer_type() window = spawn._before.getvalue() - spawn._buffer.write(spawn._before.getvalue()) + spawn._buffer.write(window) elif buf_len < self.searchwindowsize: spawn._buffer = spawn.buffer_type() spawn._before.seek( -- cgit v1.2.1