diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py index 5015b053ff29fa..5fe2850460717b 100644 --- a/Lib/test/test_curses.py +++ b/Lib/test/test_curses.py @@ -80,6 +80,12 @@ def wrapped(self, *args, **kwargs): term = os.environ.get('TERM') SHORT_MAX = 0x7fff +# ncurses before 6.5 can crash on repeated newterm(). Fall back to initscr() +# and skip the tests that need several screens. +_ncurses_version = getattr(curses, 'ncurses_version', None) +BROKEN_NEWTERM = _ncurses_version is not None and _ncurses_version < (6, 5) +USE_NEWTERM = hasattr(curses, 'newterm') and not BROKEN_NEWTERM + # newterm() is used when available (it reports errors instead of exiting), but # initscr() is still the fallback, and an unusable $TERM has no terminal to # drive either way. @@ -139,7 +145,7 @@ def setUp(self): sys.stderr.flush() sys.stdout.flush() print(file=self.output, flush=True) - if hasattr(curses, 'newterm'): + if USE_NEWTERM: # Use newterm() rather than initscr(): it reports errors instead of # exiting, and gives each test a fresh screen, which also lets # ScreenTests run newterm()/set_term() in the same process. @@ -157,7 +163,11 @@ def setUp(self): self.addCleanup(setattr, self, 'screen', None) self.addCleanup(setattr, self, 'stdscr', None) else: + # Tests share one initscr() screen; clear the rendition and + # background so a previous test's does not bleed in. self.stdscr = curses.initscr() + self.stdscr.attrset(curses.A_NORMAL) + self.stdscr.bkgdset(' ') if self.isatty: curses.savetty() self.addCleanup(curses.endwin) @@ -2026,6 +2036,7 @@ def test_use_window(self): @unittest.skipUnless(hasattr(curses.screen, 'use'), 'requires screen.use()') + @unittest.skipUnless(USE_NEWTERM, 'no screen object without newterm()') def test_use_screen(self): screen = self.screen self.assertEqual( @@ -2913,6 +2924,7 @@ def stop_reader(): @unittest.skipUnless(hasattr(curses, 'newterm'), 'requires curses.newterm()') +@unittest.skipIf(BROKEN_NEWTERM, 'ncurses < 6.5 mishandles repeated newterm()') @unittest.skipIf(not term or term == 'unknown', f"$TERM={term!r}, newterm() may not work") @unittest.skipIf(sys.platform == "cygwin", @@ -2994,6 +3006,7 @@ def test_disallow_instantiation(self): @unittest.skipUnless(hasattr(curses, 'slk_init'), 'requires curses.slk_init()') @unittest.skipUnless(hasattr(curses, 'newterm'), 'requires curses.newterm()') +@unittest.skipIf(BROKEN_NEWTERM, 'ncurses < 6.5 mishandles repeated newterm()') @unittest.skipIf(not term or term == 'unknown', f"$TERM={term!r}, newterm() may not work") @unittest.skipIf(sys.platform == "cygwin",