summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornoah <noah@656d521f-e311-0410-88e0-e7920216d269>2007-12-16 20:34:30 +0000
committernoah <noah@656d521f-e311-0410-88e0-e7920216d269>2007-12-16 20:34:30 +0000
commit4d5fd4989db93f605e45ae8a5dbc9b3dc785f59b (patch)
treef0bee4b4db61a0fc90bac20dea21c645806fe063
parent7d91918217ebffc8cd306cbaf6fefd4cda2be88f (diff)
downloadpexpect-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/geert29
-rw-r--r--pexpect/pexpect-487-expect-order.patch138
-rw-r--r--pexpect/pexpect-487-expect_exact-bugs.patch221
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()
+