diff options
author | noah <noah@656d521f-e311-0410-88e0-e7920216d269> | 2007-12-16 20:34:30 +0000 |
---|---|---|
committer | noah <noah@656d521f-e311-0410-88e0-e7920216d269> | 2007-12-16 20:34:30 +0000 |
commit | 4d5fd4989db93f605e45ae8a5dbc9b3dc785f59b (patch) | |
tree | f0bee4b4db61a0fc90bac20dea21c645806fe063 | |
parent | 7d91918217ebffc8cd306cbaf6fefd4cda2be88f (diff) | |
download | pexpect-4d5fd4989db93f605e45ae8a5dbc9b3dc785f59b.tar.gz |
Some peatches as suggestions I should add.
git-svn-id: http://pexpect.svn.sourceforge.net/svnroot/pexpect/trunk@492 656d521f-e311-0410-88e0-e7920216d269
-rw-r--r-- | pexpect/geert | 29 | ||||
-rw-r--r-- | pexpect/pexpect-487-expect-order.patch | 138 | ||||
-rw-r--r-- | pexpect/pexpect-487-expect_exact-bugs.patch | 221 |
3 files changed, 388 insertions, 0 deletions
diff --git a/pexpect/geert b/pexpect/geert new file mode 100644 index 0000000..c45928a --- /dev/null +++ b/pexpect/geert @@ -0,0 +1,29 @@ +Hello Noah, + +first of all I would like to thank you for pexpect -- it is great and I +use it all the time. + +A while back I also wrote something in the same functional space: kdesu. +This also contained a small helper in C++ for interfacing with a +command-line program through a PTY. + +When I read the description of the "echo bug" on your page, and saw how +you wanted to solve this (by issueing a delay), I remembered that there +is another way to solve this and this is how I implemented it in kdesu. +You could add a method to pexpect to i) report on the echo status of the +PTY, and 2) wait for echo to become off. The latter may require a +polling loop as I think there is no wait to wait for it (but I am not sure). + +The SSH problem you describe could then be solved like this: + + child.expect ('[pP]assword:') + child.wait_echo_off() + child.sendline (my_password) + +I think this is guaranteed to be race-free and in general feels a bit +cleaner that issueing a delay. + +Hope this was useful. In any case keep up the great work with pexpect! + +Regards, +Geert Jansen diff --git a/pexpect/pexpect-487-expect-order.patch b/pexpect/pexpect-487-expect-order.patch new file mode 100644 index 0000000..5d14238 --- /dev/null +++ b/pexpect/pexpect-487-expect-order.patch @@ -0,0 +1,138 @@ +This patch restores restores the order in which patterns match +so that it works as pexpect 2.1 did. It was my expect_exact() +patch which broke this back in June 2007. + +It also tries to document the behavior, and adds unit tests on +it. All tests should pass. test_expect.py passes when applied +to pexpect 2.1. + +It's a patch against pexpect SVN 487, but requires +pexpect-487-expect_exact-bugs.patch to have been applied first. + +/Jörgen + + +diff -aur p/pexpect/pexpect.py pexpect/pexpect/pexpect.py +--- p/pexpect/pexpect.py 2007-11-18 16:31:57.000000000 +0100 ++++ pexpect/pexpect/pexpect.py 2007-11-18 15:30:31.000000000 +0100 +@@ -1187,6 +1187,22 @@ + list. That will cause expect to match an EOF or TIMEOUT condition + instead of raising an exception. + ++ If you pass a list of patterns and more than one matches, the first match ++ in the stream is chosen. If more than one pattern matches at that point, ++ the leftmost in the pattern list is chosen. For example:: ++ ++ # the input is 'foobar' ++ index = p.expect (['bar', 'foo', 'foobar']) ++ # returns 1 ('foo') even though 'foobar' is a "better" match ++ ++ Please note, however, that buffering can affect this behavior, since ++ input arrives in unpredictable chunks. For example:: ++ ++ # the input is 'foobar' ++ index = p.expect (['foobar', 'foo']) ++ # returns 0 ('foobar') if all input is available at once, ++ # but returs 1 ('foo') if parts of the final 'bar' arrive late ++ + After a match is found the instance attributes 'before', 'after' and + 'match' will be set. You can see all the data read before the match in + 'before'. You can see the data that was matched in 'after'. The +@@ -1588,8 +1604,7 @@ + # better obey searchwindowsize + offset = -searchwindowsize + n = buffer.find(s, offset) +- if n >= 0 and n <= first_match: +- # note that the last, not the longest, match rules ++ if n >= 0 and n < first_match: + first_match = n + best_index, best_match = index, s + if first_match == absurd_match: +@@ -1669,8 +1684,7 @@ + if match is None: + continue + n = match.start() +- if n <= first_match: +- # note that the last, not the longest, match rules ++ if n < first_match: + first_match = n + the_match = match + best_index = index +Only in p/pexpect: pexpect.py.rej +diff -aur p/pexpect/tests/test_expect.py pexpect/pexpect/tests/test_expect.py +--- p/pexpect/tests/test_expect.py 2007-11-18 16:31:57.000000000 +0100 ++++ pexpect/pexpect/tests/test_expect.py 2007-11-18 16:06:39.000000000 +0100 +@@ -230,8 +230,6 @@ + else: + self.fail ('Expected an EOF exception.') + +-class AdditionalExpectTestCase (PexpectTestCase.PexpectTestCase): +- + def _before_after(self, p): + p.timeout = 5 + +@@ -307,6 +305,64 @@ + p.expect = p.expect_exact + self._ordering(p) + ++ def _greed(self, p): ++ p.timeout = 5 ++ p.expect('>>> ') ++ p.sendline('import time') ++ p.expect('>>> ') ++ # the newline and sleep will (I hope) guarantee that ++ # pexpect is fed two distinct batches of data, ++ # "foo\r\n" + "bar\r\n". ++ foo_then_bar = 'print "f"+"o"+"o" ; time.sleep(3); print "b"+"a"+"r"' ++ ++ p.sendline(foo_then_bar) ++ self.assertEqual(p.expect(['foo\r\nbar']), 0) ++ p.expect('>>> ') ++ ++ p.sendline(foo_then_bar) ++ self.assertEqual(p.expect(['\r\nbar']), 0) ++ p.expect('>>> ') ++ ++ p.sendline(foo_then_bar) ++ self.assertEqual(p.expect(['foo\r\nbar', 'foo', 'bar']), 1) ++ p.expect('>>> ') ++ ++ p.sendline(foo_then_bar) ++ self.assertEqual(p.expect(['foo', 'foo\r\nbar', 'foo', 'bar']), 0) ++ p.expect('>>> ') ++ ++ p.sendline(foo_then_bar) ++ self.assertEqual(p.expect(['bar', 'foo\r\nbar']), 1) ++ p.expect('>>> ') ++ ++ # If the expect works as if we rematch for every new character, ++ # 'o\r\nb' should win over 'oo\r\nba'. The latter is longer and ++ # matches earlier in the input, but isn't satisfied until the 'a' ++ # arrives. ++ # However, pexpect doesn't do that (version 2.1 didn't). ++ p.sendline(foo_then_bar) ++ self.assertEqual(p.expect(['oo\r\nba', 'o\r\nb']), 0) ++ p.expect('>>> ') ++ ++ # distinct patterns, but both suddenly match when the 'r' arrives. ++ p.sendline(foo_then_bar) ++ self.assertEqual(p.expect(['foo\r\nbar', 'ar']), 0) ++ p.expect('>>> ') ++ ++ p.sendline(foo_then_bar) ++ self.assertEqual(p.expect(['ar', 'foo\r\nbar']), 1) ++ p.expect('>>> ') ++ ++ def test_greed(self): ++ p = pexpect.spawn(self.PYTHONBIN) ++ self._greed(p) ++ ++ def test_greed_exact(self): ++ p = pexpect.spawn(self.PYTHONBIN) ++ # mangle the spawn so we test expect_exact() instead ++ p.expect = p.expect_exact ++ self._greed(p) ++ + if __name__ == '__main__': + unittest.main() + diff --git a/pexpect/pexpect-487-expect_exact-bugs.patch b/pexpect/pexpect-487-expect_exact-bugs.patch new file mode 100644 index 0000000..6232fcf --- /dev/null +++ b/pexpect/pexpect-487-expect_exact-bugs.patch @@ -0,0 +1,221 @@ +Some fixes related to the expect_exact() patches I sent back in June. +As you see, they are against pexpect, SVN 487. + +- Bug fix: expect_exact() was broken for a single TIMEOUT or EOF pattern, + and for lists of patterns, where TIMEOUT or EOF were in the list, + before a normal string pattern. + +- Added tests to expose these bugs (by splicing into those tests + which excercised expect() and didn't use REs). + +- Added some more tests for regressions since 2.1, in which pattern + is chosen as the match when many match -- something Noah brought up + earlier this year. I plan to send another patch to "fix" this later + today. + +/Jörgen + +Index: pexpect.py +=================================================================== +--- pexpect.py (revision 487) ++++ pexpect.py (working copy) +@@ -35,7 +35,7 @@ + vander Molen, George Todd, Noel Taylor, Nicolas D. Cesar, Alexander Gattin, + Geoffrey Marshall, Francisco Lourenco, Glen Mabey, Karthik Gurusamy, Fernando + Perez, Corey Minyard, Jon Cohen, Guillaume Chazarain, Andrew Ryan, Nick +-Craig-Wood, Andrew Stone (Let me know if I forgot anyone.) ++Craig-Wood, Andrew Stone, Jorgen Grahn (Let me know if I forgot anyone.) + + Free, open source, and all that good stuff. + +@@ -1267,7 +1267,7 @@ + This method is also useful when you don't want to have to worry about + escaping regular expression characters that you want to match.""" + +- if type(pattern_list) in types.StringTypes + (TIMEOUT, EOF): ++ if type(pattern_list) in types.StringTypes or pattern_list in (TIMEOUT, EOF): + pattern_list = [pattern_list] + return self.expect_loop(searcher_string(pattern_list), timeout, searchwindowsize) + +@@ -1585,16 +1585,16 @@ + # or at the very end of the old data + offset = -(freshlen+len(s)) + else: +- # better obey_searchwindowsize ++ # better obey searchwindowsize + offset = -searchwindowsize + n = buffer.find(s, offset) + if n >= 0 and n <= first_match: + # note that the last, not the longest, match rules + first_match = n +- best_index = index ++ best_index, best_match = index, s + if first_match == absurd_match: + return -1 +- self.match = self._strings[best_index][1] ++ self.match = best_match + self.start = first_match + self.end = self.start + len(self.match) + return best_index +Index: tests/test_expect.py +=================================================================== +--- tests/test_expect.py (revision 487) ++++ tests/test_expect.py (working copy) +@@ -34,6 +34,17 @@ + p.sendeof () + p.expect (pexpect.EOF) + ++ def test_expect_exact_basic (self): ++ p = pexpect.spawn('cat') ++ p.sendline ('Hello') ++ p.sendline ('there') ++ p.sendline ('Mr. Python') ++ p.expect_exact ('Hello') ++ p.expect_exact ('there') ++ p.expect_exact ('Mr. Python') ++ p.sendeof () ++ p.expect_exact (pexpect.EOF) ++ + def test_expect_ignore_case(self): + """This test that the ignorecase flag will match patterns + even if case is different using the regex (?i) directive. +@@ -61,8 +72,21 @@ + + def test_expect_order (self): + """This tests that patterns are matched in the same order as given in the pattern_list. ++ ++ (Or does it? Doesn't it also pass if expect() always chooses ++ (one of the) the leftmost matches in the input? -- grahn) + """ + p = pexpect.spawn('cat') ++ self._expect_order(p) ++ ++ def test_expect_order_exact (self): ++ """Like test_expect_order(), but using expect_exact(). ++ """ ++ p = pexpect.spawn('cat') ++ p.expect = p.expect_exact ++ self._expect_order(p) ++ ++ def _expect_order (self, p): + p.sendline ('1234') + p.sendline ('abcd') + p.sendline ('wxyz') +@@ -89,6 +113,16 @@ + """This tests that echo can be turned on and off. + """ + p = pexpect.spawn('cat', timeout=10) ++ self._expect_echo(p) ++ ++ def test_expect_echo_exact (self): ++ """Like test_expect_echo(), but using expect_exact(). ++ """ ++ p = pexpect.spawn('cat', timeout=10) ++ p.expect = p.expect_exact ++ self._expect_echo(p) ++ ++ def _expect_echo (self, p): + p.sendline ('1234') # Should see this twice (once from tty echo and again from cat). + index = p.expect (['1234','abcd','wxyz',pexpect.EOF,pexpect.TIMEOUT]) + assert index == 0, "index="+str(index)+"\n"+p.before +@@ -115,6 +149,16 @@ + """ + #pdb.set_trace() + p = pexpect.spawn('cat') ++ self._expect_index(p) ++ ++ def test_expect_index_exact (self): ++ """Like test_expect_index(), but using expect_exact(). ++ """ ++ p = pexpect.spawn('cat') ++ p.expect = p.expect_exact ++ self._expect_index(p) ++ ++ def _expect_index (self, p): + p.setecho(0) + p.sendline ('1234') + index = p.expect (['abcd','wxyz','1234',pexpect.EOF]) +@@ -186,6 +230,83 @@ + else: + self.fail ('Expected an EOF exception.') + ++class AdditionalExpectTestCase (PexpectTestCase.PexpectTestCase): ++ ++ def _before_after(self, p): ++ p.timeout = 5 ++ ++ p.expect('>>> ') ++ self.assertEqual(p.after, '>>> ') ++ self.assert_(p.before.startswith('Python ')) ++ ++ p.sendline('range(4*3)') ++ ++ p.expect('5') ++ self.assertEqual(p.after, '5') ++ self.assert_(p.before.startswith('range(4*3)')) ++ ++ p.expect('>>> ') ++ self.assertEqual(p.after, '>>> ') ++ self.assert_(p.before.startswith(', 6, 7, 8')) ++ ++ def test_before_after(self): ++ """This tests expect() for some simple before/after things. ++ """ ++ p = pexpect.spawn(self.PYTHONBIN) ++ 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.) ++ """ ++ p = pexpect.spawn(self.PYTHONBIN) ++ # mangle the spawn so we test expect_exact() instead ++ p.expect = p.expect_exact ++ self._before_after(p) ++ ++ def _ordering(self, p): ++ p.timeout = 5 ++ p.expect('>>> ') ++ ++ p.sendline('range(4*3)') ++ self.assertEqual(p.expect(['5,', '5,']), 0) ++ p.expect('>>> ') ++ ++ p.sendline('range(4*3)') ++ self.assertEqual(p.expect(['7,', '5,']), 1) ++ p.expect('>>> ') ++ ++ p.sendline('range(4*3)') ++ self.assertEqual(p.expect(['5,', '7,']), 0) ++ p.expect('>>> ') ++ ++ p.sendline('range(4*5)') ++ self.assertEqual(p.expect(['2,', '12,']), 0) ++ p.expect('>>> ') ++ ++ p.sendline('range(4*5)') ++ self.assertEqual(p.expect(['12,', '2,']), 1) ++ ++ def test_ordering(self): ++ """This tests expect() for which pattern is returned ++ when many may eventually match. I (Grahn) am a bit ++ confused about what should happen, but this test passes ++ with pexpect 2.1. ++ """ ++ p = pexpect.spawn(self.PYTHONBIN) ++ self._ordering(p) ++ ++ def test_ordering_exact(self): ++ """This tests expect_exact() for which pattern is returned ++ when many may eventually match. I (Grahn) am a bit ++ confused about what should happen, but this test passes ++ for the expect() method with pexpect 2.1. ++ """ ++ p = pexpect.spawn(self.PYTHONBIN) ++ # mangle the spawn so we test expect_exact() instead ++ p.expect = p.expect_exact ++ self._ordering(p) ++ + if __name__ == '__main__': + unittest.main() + |