Repository navigation
test_curses fails in every Ubuntu CI run's parallel phase: test_use_prescr_screen leaves use_env(False) set, so newterm() fails with TERM=linux #157170
Description
Bug report
Bug description:
On every recent Ubuntu CI job on main, test_curses fails in the parallel phase with 138 errors and only passes on the automatic sequential rerun, so the failure never shows up as a red check. I sampled the last 8 successful Tests runs: all 31 Ubuntu job runs (x86-64 and arm64, default and free-threading builds) show test_curses failed (138 errors) followed by test_curses passed on the rerun. Example, the free-threading job of https://github.com/python/cpython/actions/runs/34185684639: test_curses failed (138 errors) at 0:11:27 and [1/1] test_curses passed at 0:13:58. Every error is the same:
ERROR: test_add_string_behavior (test.test_curses.TestCurses.test_add_string_behavior)
File ".../Lib/test/test_curses.py", line 183, in setUp
self.screen = curses.newterm(term, stdout_fd, infd)
curses.error: newterm() returned NULL
The 138 errors are every test of TestCurses (and the textbox/panel tests that share its setUp()), so in effect those tests only run in the rerun, and only there because the rerun re-runs the failed tests by name and therefore skips ScreenTests.
Cause
ScreenTests.test_use_prescr_screen ends with
# Affecting the state before initscr() is what such a screen is for.
prescr.use(lambda scr: curses.use_env(False))and never turns use_env back on. curses.use_env() calls ncurses' use_env(), which sets the process-wide default _nc_prescreen.use_env, not a property of the screen that is current when it is called, and every screen created afterwards copies that default. With use_env(False), ncurses takes the screen size from the terminfo lines and cols capabilities alone, without the LINES/COLUMNS environment or the 24x80 fallback. The CI runners have TERM=linux, and the linux terminfo entry has no lines or cols:
$ infocmp -1 linux | grep -E 'lines#|cols#' # nothing
$ infocmp -1 xterm | grep -E 'lines#|cols#' # cols#80 lines#24
so every newterm() after that test computes a -1x-1 screen and returns NULL. With TERM=xterm or vt100 the size comes from terminfo and the module passes, which is why this does not show up on developer machines.
I confirmed this with an ncurses 6.5 built with --with-trace. For a newterm("linux", ...) after test_use_prescr_screen:
called {newterm(0xba7a54022bc0, "linux", 0xba7a53dd7120,0xba7a53d4a6c0)
screen size: terminfo lines = -1 columns = -1
useEnv:0 useTioctl:0
screen size is -1x-1
whereas before that test, or after restoring use_env(True), it reads useEnv:1 and screen size is 24x80.
Reproducer
On Ubuntu 25.10 (ncurses 6.5, so the newterm()-based setUp() is used) with a debug build of main:
$ TERM=linux ./python -m test test_curses -u curses # test_curses failed (138 errors)
$ TERM=xterm ./python -m test test_curses -u curses # test_curses passed
Restoring the setting in the test (self.addCleanup(curses.use_env, True) before the prescr.use(...) line) makes the module pass with TERM=linux, and a --fast-ci run then needs no rerun. I will open a PR.
The docs for curses.use_env() could also say that the setting is process-wide and not tied to a screen, since the comment in the test shows the opposite was assumed.
CPython versions tested on:
CPython main branch
Operating systems tested on:
Linux
Activity
iamsharduld commentedon Sep 8, 2026
Confirmed on CI with #157171: in its Tests run (https://github.com/python/cpython/actions/runs/34194369258), all four Ubuntu jobs (x86-64 and arm64, default and free-threading) report
test_curses passedin the parallel phase, and none of them has a rerun phase. Before the change, every sampled run hadtest_curses failed (138 errors)there and passed only on the rerun.gh-157170: Restore use_env() after test_use_prescr_screen in test_cur…
gh-157170: Document the scope of global curses settings (GH-157207)
Merge branch 'main' into tachyon-native-callable
pythongh-157170: Restore use_env() after test_use_prescr_screen in te…
pythongh-157170: Document the scope of global curses settings (python…
pythongh-157170: Restore use_env() after test_use_prescr_screen in te…
pythongh-157170: Document the scope of global curses settings (python…