[Python-checkins] cpython (merge 3.5 -> 3.6): Issue #13051: Fixed recursion errors in large or resized curses.textpad.Textbox.

serhiy.storchaka python-checkins at python.org
Wed Dec 28 03:23:40 EST 2016


https://hg.python.org/cpython/rev/ea87e00a3e89
changeset:   105873:ea87e00a3e89
branch:      3.6
parent:      105869:eb81f2d2a42b
parent:      105871:b446a4aab9cf
user:        Serhiy Storchaka <storchaka at gmail.com>
date:        Wed Dec 28 10:22:56 2016 +0200
summary:
  Issue #13051: Fixed recursion errors in large or resized curses.textpad.Textbox.
Based on patch by Tycho Andersen.

files:
  Lib/curses/textpad.py   |  31 ++++++++++++++++++++--------
  Lib/test/test_curses.py |   9 ++++++++
  Misc/ACKS               |   1 +
  Misc/NEWS               |   3 ++
  4 files changed, 35 insertions(+), 9 deletions(-)


diff --git a/Lib/curses/textpad.py b/Lib/curses/textpad.py
--- a/Lib/curses/textpad.py
+++ b/Lib/curses/textpad.py
@@ -43,16 +43,20 @@
     def __init__(self, win, insert_mode=False):
         self.win = win
         self.insert_mode = insert_mode
-        (self.maxy, self.maxx) = win.getmaxyx()
-        self.maxy = self.maxy - 1
-        self.maxx = self.maxx - 1
+        self._update_max_yx()
         self.stripspaces = 1
         self.lastcmd = None
         win.keypad(1)
 
+    def _update_max_yx(self):
+        maxy, maxx = self.win.getmaxyx()
+        self.maxy = maxy - 1
+        self.maxx = maxx - 1
+
     def _end_of_line(self, y):
         """Go to the location of the first blank on the given line,
         returning the index of the last non-blank character."""
+        self._update_max_yx()
         last = self.maxx
         while True:
             if curses.ascii.ascii(self.win.inch(y, last)) != curses.ascii.SP:
@@ -64,8 +68,10 @@
         return last
 
     def _insert_printable_char(self, ch):
+        self._update_max_yx()
         (y, x) = self.win.getyx()
-        if y < self.maxy or x < self.maxx:
+        backyx = None
+        while y < self.maxy or x < self.maxx:
             if self.insert_mode:
                 oldch = self.win.inch()
             # The try-catch ignores the error we trigger from some curses
@@ -75,14 +81,20 @@
                 self.win.addch(ch)
             except curses.error:
                 pass
-            if self.insert_mode:
-                (backy, backx) = self.win.getyx()
-                if curses.ascii.isprint(oldch):
-                    self._insert_printable_char(oldch)
-                    self.win.move(backy, backx)
+            if not self.insert_mode or not curses.ascii.isprint(oldch):
+                break
+            ch = oldch
+            (y, x) = self.win.getyx()
+            # Remember where to put the cursor back since we are in insert_mode
+            if backyx is None:
+                backyx = y, x
+
+        if backyx is not None:
+            self.win.move(*backyx)
 
     def do_command(self, ch):
         "Process a single editing command."
+        self._update_max_yx()
         (y, x) = self.win.getyx()
         self.lastcmd = ch
         if curses.ascii.isprint(ch):
@@ -148,6 +160,7 @@
     def gather(self):
         "Collect and return the contents of the window."
         result = ""
+        self._update_max_yx()
         for y in range(self.maxy+1):
             self.win.move(y, 0)
             stop = self._end_of_line(y)
diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py
--- a/Lib/test/test_curses.py
+++ b/Lib/test/test_curses.py
@@ -27,6 +27,7 @@
 curses = import_module('curses')
 import_module('curses.panel')
 import_module('curses.ascii')
+import_module('curses.textpad')
 
 def requires_curses_func(name):
     return unittest.skipUnless(hasattr(curses, name),
@@ -392,6 +393,14 @@
         human_readable_signature = stdscr.addch.__doc__.split("\n")[0]
         self.assertIn("[y, x,]", human_readable_signature)
 
+    def test_issue13051(self):
+        stdscr = self.stdscr
+        box = curses.textpad.Textbox(stdscr, insert_mode=True)
+        lines, cols = stdscr.getmaxyx()
+        stdscr.resize(lines-2, cols-2)
+        # this may cause infinite recursion, leading to a RuntimeError
+        box._insert_printable_char('a')
+
 
 class MiscTests(unittest.TestCase):
 
diff --git a/Misc/ACKS b/Misc/ACKS
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -41,6 +41,7 @@
 Mark Anacker
 Shashwat Anand
 Anders Andersen
+Tycho Andersen
 John Anderson
 Pehr Anderson
 Erik Andersén
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -40,6 +40,9 @@
 Library
 -------
 
+- Issue #13051: Fixed recursion errors in large or resized
+  curses.textpad.Textbox.  Based on patch by Tycho Andersen.
+
 - Issue #9770: curses.ascii predicates now work correctly with negative
   integers.
 

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list