]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-46927: Prevent readline from overriding environment variables (GH-153184)
authorMathieu Dubois-Briand <mathieu.dubois-briand@hyprua.org>
Wed, 8 Jul 2026 16:45:15 +0000 (18:45 +0200)
committerGitHub <noreply@github.com>
Wed, 8 Jul 2026 16:45:15 +0000 (16:45 +0000)
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 <mathieu.dubois-briand@bootlin.com>
Lib/test/test_readline.py
Misc/NEWS.d/next/Library/2025-05-07-18-31-31.gh-issue-46927.sF02gj.rst [new file with mode: 0644]
Modules/readline.c
configure
configure.ac
pyconfig.h.in

index 3982686dd10aecf683cd3a8c8436d39af1e67320..b0b9d64cfe6a5f7a0b94b2ca241c0cfa7ef96838 100644 (file)
@@ -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 (file)
index 0000000..3ba9757
--- /dev/null
@@ -0,0 +1,2 @@
+Prevent :mod:`readline` from overriding the ``COLUMNS`` and ``LINES``
+environment variables, as values are not updated on terminal resize.
index c580d2022fccf3d6202865510e30f4e302eff35d..4c965e081c9d1a77ea1414142f154ac865c7568b 100644 (file)
@@ -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
      */
index 76c58f7c3a463dbc22ce0a4daf64ea360567c73d..363058cf3e8acf2ce777c037eb9fca50281e036c 100755 (executable)
--- 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
index de7a3abb379e659d29ac613c2f18af4b61246e0d..0a734f9d82a1f4d534fe4c027ed8fb41bf550db3 100644 (file)
@@ -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(
index ce97099315bfe4ad0be6db5f194e189d687c81ab..a619672c47e5b56d544f41382dbba90ab0b4026f 100644 (file)
 /* 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