diff --git a/Lib/test/test_readline.py b/Lib/test/test_readline.py index 3982686dd10aec..1d87890860911b 100644 --- a/Lib/test/test_readline.py +++ b/Lib/test/test_readline.py @@ -413,6 +413,16 @@ def test_write_read_limited_history(self): # 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 enviroment 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/Core_and_Builtins/2025-05-07-18-31-31.gh-issue-46927.sF02gj.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-07-18-31-31.gh-issue-46927.sF02gj.rst new file mode 100644 index 00000000000000..3ba9757c8acb5d --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/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 c580d2022fccf3..4c965e081c9d1a 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 01faef615a3d5e..2f03f7199ec23c 100755 --- a/configure +++ b/configure @@ -28638,6 +28638,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 a9fe5c269618fc..cc67b52d69d5cd 100644 --- a/configure.ac +++ b/configure.ac @@ -6830,6 +6830,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 ce97099315bfe4..a619672c47e5b5 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