From: Mathieu Dubois-Briand Date: Wed, 8 Jul 2026 16:45:15 +0000 (+0200) Subject: gh-46927: Prevent readline from overriding environment variables (GH-153184) X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c9687b484102ca6dbc3489fe1e5773e0cefa8f51;p=thirdparty%2FPython%2Fcpython.git gh-46927: Prevent readline from overriding environment variables (GH-153184) Readline sets COLUMNS and LINES to the terminal size at initialization, and ncurses prefers those stale variables over an ioctl() query, breaking resize handling. Set rl_change_environment to 0, when available, so importing readline no longer modifies the environment. Signed-off-by: Mathieu Dubois-Briand --- diff --git a/Lib/test/test_readline.py b/Lib/test/test_readline.py index 3982686dd10a..b0b9d64cfe6a 100644 --- a/Lib/test/test_readline.py +++ b/Lib/test/test_readline.py @@ -413,6 +413,16 @@ readline.write_history_file(history_file) # So, we've only tested that the read did not fail. # See TestHistoryManipulation for the full test. + def test_environment_is_not_modified(self): + # os.environ contains environment at the time "os" module was loaded, so + # before the "readline" module is loaded. + original_env = dict(os.environ) + + # Force refresh of os.environ and make sure it is the same as before the + # refresh. + os.reload_environ() + self.assertEqual(dict(os.environ), original_env) + @unittest.skipUnless(hasattr(readline, "get_pre_input_hook"), "get_pre_input_hook not available") def test_get_pre_input_hook(self): diff --git a/Misc/NEWS.d/next/Library/2025-05-07-18-31-31.gh-issue-46927.sF02gj.rst b/Misc/NEWS.d/next/Library/2025-05-07-18-31-31.gh-issue-46927.sF02gj.rst new file mode 100644 index 000000000000..3ba9757c8acb --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-05-07-18-31-31.gh-issue-46927.sF02gj.rst @@ -0,0 +1,2 @@ +Prevent :mod:`readline` from overriding the ``COLUMNS`` and ``LINES`` +environment variables, as values are not updated on terminal resize. diff --git a/Modules/readline.c b/Modules/readline.c index c580d2022fcc..4c965e081c9d 100644 --- a/Modules/readline.c +++ b/Modules/readline.c @@ -1351,6 +1351,13 @@ setup_readline(readlinestate *mod_state) /* The name must be defined before initialization */ rl_readline_name = "python"; +#ifdef HAVE_RL_CHANGE_ENVIRONMENT + /* Prevent readline from setting the LINES and COLUMNS environment + * variables: ncurses prefers them over an ioctl() query, so a stale value + * left after a resize breaks SIGWINCH / KEY_RESIZE handling (gh-46927). */ + rl_change_environment = 0; +#endif + /* the libedit readline emulation resets key bindings etc * when calling rl_initialize. So call it upfront */ diff --git a/configure b/configure index 76c58f7c3a46..363058cf3e8a 100755 --- a/configure +++ b/configure @@ -28636,6 +28636,57 @@ then : printf "%s\n" "#define HAVE_RL_RESIZE_TERMINAL 1" >>confdefs.h +fi + + # rl_change_environment is in readline 6.3, but not in editline + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for rl_change_environment in -l$LIBREADLINE" >&5 +printf %s "checking for rl_change_environment in -l$LIBREADLINE... " >&6; } +if test ${ac_cv_readline_rl_change_environment+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include /* Must be first for Gnu Readline */ + #ifdef WITH_EDITLINE + # include + #else + # include + # include + #endif + +int +main (void) +{ +int x = rl_change_environment + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO" +then : + ac_cv_readline_rl_change_environment=yes +else case e in #( + e) ac_cv_readline_rl_change_environment=no + ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext + ;; +esac +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_readline_rl_change_environment" >&5 +printf "%s\n" "$ac_cv_readline_rl_change_environment" >&6; } + if test "x$ac_cv_readline_rl_change_environment" = xyes +then : + + +printf "%s\n" "#define HAVE_RL_CHANGE_ENVIRONMENT 1" >>confdefs.h + + fi # check for readline 4.2 diff --git a/configure.ac b/configure.ac index de7a3abb379e..0a734f9d82a1 100644 --- a/configure.ac +++ b/configure.ac @@ -6826,6 +6826,17 @@ AS_VAR_IF([with_readline], [no], [ AC_DEFINE([HAVE_RL_RESIZE_TERMINAL], [1], [Define if you have readline 4.0]) ]) + # rl_change_environment is in readline 6.3, but not in editline + AC_CACHE_CHECK([for rl_change_environment in -l$LIBREADLINE], [ac_cv_readline_rl_change_environment], [ + AC_LINK_IFELSE( + [AC_LANG_PROGRAM([readline_includes], [int x = rl_change_environment])], + [ac_cv_readline_rl_change_environment=yes], [ac_cv_readline_rl_change_environment=no] + ) + ]) + AS_VAR_IF([ac_cv_readline_rl_change_environment], [yes], [ + AC_DEFINE([HAVE_RL_CHANGE_ENVIRONMENT], [1], [Define if you have readline 6.3]) + ]) + # check for readline 4.2 AC_CACHE_CHECK([for rl_completion_matches in -l$LIBREADLINE], [ac_cv_readline_rl_completion_matches], [ AC_LINK_IFELSE( diff --git a/pyconfig.h.in b/pyconfig.h.in index ce97099315bf..a619672c47e5 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -1153,6 +1153,9 @@ /* Define if you can turn off readline's signal handling. */ #undef HAVE_RL_CATCH_SIGNAL +/* Define if you have readline 6.3 */ +#undef HAVE_RL_CHANGE_ENVIRONMENT + /* Define to 1 if the system has the type 'rl_compdisp_func_t'. */ #undef HAVE_RL_COMPDISP_FUNC_T