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):
|
def __init__(self, win, insert_mode=False):
|
||||||
self.win = win
|
self.win = win
|
||||||
self.insert_mode = insert_mode
|
self.insert_mode = insert_mode
|
||||||
(self.maxy, self.maxx) = win.getmaxyx()
|
self._update_max_yx()
|
||||||
self.maxy = self.maxy - 1
|
|
||||||
self.maxx = self.maxx - 1
|
|
||||||
self.stripspaces = 1
|
self.stripspaces = 1
|
||||||
self.lastcmd = None
|
self.lastcmd = None
|
||||||
win.keypad(1)
|
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):
|
def _end_of_line(self, y):
|
||||||
"""Go to the location of the first blank on the given line,
|
"""Go to the location of the first blank on the given line,
|
||||||
returning the index of the last non-blank character."""
|
returning the index of the last non-blank character."""
|
||||||
|
self._update_max_yx()
|
||||||
last = self.maxx
|
last = self.maxx
|
||||||
while True:
|
while True:
|
||||||
if curses.ascii.ascii(self.win.inch(y, last)) != curses.ascii.SP:
|
if curses.ascii.ascii(self.win.inch(y, last)) != curses.ascii.SP:
|
||||||
|
@ -64,8 +68,10 @@ class Textbox:
|
||||||
return last
|
return last
|
||||||
|
|
||||||
def _insert_printable_char(self, ch):
|
def _insert_printable_char(self, ch):
|
||||||
|
self._update_max_yx()
|
||||||
(y, x) = self.win.getyx()
|
(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:
|
if self.insert_mode:
|
||||||
oldch = self.win.inch()
|
oldch = self.win.inch()
|
||||||
# The try-catch ignores the error we trigger from some curses
|
# The try-catch ignores the error we trigger from some curses
|
||||||
|
@ -75,14 +81,20 @@ class Textbox:
|
||||||
self.win.addch(ch)
|
self.win.addch(ch)
|
||||||
except curses.error:
|
except curses.error:
|
||||||
pass
|
pass
|
||||||
if self.insert_mode:
|
if not self.insert_mode or not curses.ascii.isprint(oldch):
|
||||||
(backy, backx) = self.win.getyx()
|
break
|
||||||
if curses.ascii.isprint(oldch):
|
ch = oldch
|
||||||
self._insert_printable_char(oldch)
|
(y, x) = self.win.getyx()
|
||||||
self.win.move(backy, backx)
|
# 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):
|
def do_command(self, ch):
|
||||||
"Process a single editing command."
|
"Process a single editing command."
|
||||||
|
self._update_max_yx()
|
||||||
(y, x) = self.win.getyx()
|
(y, x) = self.win.getyx()
|
||||||
self.lastcmd = ch
|
self.lastcmd = ch
|
||||||
if curses.ascii.isprint(ch):
|
if curses.ascii.isprint(ch):
|
||||||
|
@ -148,6 +160,7 @@ class Textbox:
|
||||||
def gather(self):
|
def gather(self):
|
||||||
"Collect and return the contents of the window."
|
"Collect and return the contents of the window."
|
||||||
result = ""
|
result = ""
|
||||||
|
self._update_max_yx()
|
||||||
for y in range(self.maxy+1):
|
for y in range(self.maxy+1):
|
||||||
self.win.move(y, 0)
|
self.win.move(y, 0)
|
||||||
stop = self._end_of_line(y)
|
stop = self._end_of_line(y)
|
||||||
|
|
|
@ -27,6 +27,7 @@ requires('curses')
|
||||||
curses = import_module('curses')
|
curses = import_module('curses')
|
||||||
import_module('curses.panel')
|
import_module('curses.panel')
|
||||||
import_module('curses.ascii')
|
import_module('curses.ascii')
|
||||||
|
import_module('curses.textpad')
|
||||||
|
|
||||||
def requires_curses_func(name):
|
def requires_curses_func(name):
|
||||||
return unittest.skipUnless(hasattr(curses, name),
|
return unittest.skipUnless(hasattr(curses, name),
|
||||||
|
@ -392,6 +393,14 @@ class TestCurses(unittest.TestCase):
|
||||||
human_readable_signature = stdscr.addch.__doc__.split("\n")[0]
|
human_readable_signature = stdscr.addch.__doc__.split("\n")[0]
|
||||||
self.assertIn("[y, x,]", human_readable_signature)
|
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):
|
class MiscTests(unittest.TestCase):
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,7 @@ A. Amoroso
|
||||||
Mark Anacker
|
Mark Anacker
|
||||||
Shashwat Anand
|
Shashwat Anand
|
||||||
Anders Andersen
|
Anders Andersen
|
||||||
|
Tycho Andersen
|
||||||
John Anderson
|
John Anderson
|
||||||
Pehr Anderson
|
Pehr Anderson
|
||||||
Erik Andersén
|
Erik Andersén
|
||||||
|
|
|
@ -140,6 +140,9 @@ Core and Builtins
|
||||||
Library
|
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
|
- Issue #9770: curses.ascii predicates now work correctly with negative
|
||||||
integers.
|
integers.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue