Issue #13051: Fixed recursion errors in large or resized curses.textpad.Textbox.
Based on patch by Tycho Andersen.
This commit is contained in:
parent
283de2b9c1
commit
bdf9e0ea74
|
@ -43,16 +43,20 @@ class Textbox:
|
|||
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 @@ class Textbox:
|
|||
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 @@ class Textbox:
|
|||
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 @@ class Textbox:
|
|||
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)
|
||||
|
|
|
@ -27,6 +27,7 @@ requires('curses')
|
|||
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 @@ class TestCurses(unittest.TestCase):
|
|||
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):
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ A. Amoroso
|
|||
Mark Anacker
|
||||
Shashwat Anand
|
||||
Anders Andersen
|
||||
Tycho Andersen
|
||||
John Anderson
|
||||
Pehr Anderson
|
||||
Erik Andersén
|
||||
|
|
Loading…
Reference in New Issue