https://github.com/python/cpython/commit/c9687b484102ca6dbc3489fe1e5773e0cefa8f51
commit: c9687b484102ca6dbc3489fe1e5773e0cefa8f51
branch: main
author: Mathieu Dubois-Briand <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-07-08T16:45:15Z
summary:

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 <[email protected]>

files:
A Misc/NEWS.d/next/Library/2025-05-07-18-31-31.gh-issue-46927.sF02gj.rst
M Lib/test/test_readline.py
M Modules/readline.c
M configure
M configure.ac
M pyconfig.h.in

diff --git a/Lib/test/test_readline.py b/Lib/test/test_readline.py
index 3982686dd10aec..b0b9d64cfe6a5f 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 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 00000000000000..3ba9757c8acb5d
--- /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 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 76c58f7c3a463d..363058cf3e8acf 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 <stdio.h> /* Must be first for Gnu Readline */
+      #ifdef WITH_EDITLINE
+      # include <editline/readline.h>
+      #else
+      # include <readline/readline.h>
+      # include <readline/history.h>
+      #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 de7a3abb379e65..0a734f9d82a1f4 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 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
 

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to