]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
[gdb/python] Handle error in gdbpy_initialize_gdb_readline
authorTom de Vries <tdevries@suse.de>
Wed, 12 Aug 2026 21:11:46 +0000 (23:11 +0200)
committerTom de Vries <tdevries@suse.de>
Wed, 12 Aug 2026 21:11:46 +0000 (23:11 +0200)
On Fedora Rawhide aarch64-linux, with test-case gdb.python/py-failed-init.exp
I ran into:
...
builtin_spawn $build/gdb/gdb -nw -nx -q -iex set height 0 -iex set width 0 \
  -data-directory $build/gdb/data-directory -iex set interactive-mode on
WARN: Could not find the standard library directory! The Python 'home' \
  directory was set to 'foo', is this correct?
Error occurred computing Python error message.
$build/gdb/gdb: warning:
Could not load the Python gdb module from `$build/gdb/data-directory/python'.
Limited Python support is available from the _gdb module.
Suggest passing --data-directory=/path/to/gdb/data-directory.
(gdb) set height 0
(gdb) set width 0
(gdb) dir
Reinitialize source path to empty? (y or n) y
Source directories searched: $cdir:$cwd
(gdb) dir $src/gdb/testsuite/gdb.python
Source directories searched: $src/gdb/testsuite/gdb.python:$cdir:$cwd
(gdb) python print (1)
1
(gdb) FAIL: $exp: gdb-command<python print (1)>
quit
Exception ignored on threading shutdown:
Traceback (most recent call last):
  File "<string>", line 2, in <module>
ModuleNotFoundError: No module named 'importlib'
PASS: $exp: quit
...

The test-case tries to break python:
...
save_vars { env(PYTHONHOME) } {
    setenv PYTHONHOME foo
    clean_restart
}
...
enough to get it to this point:
...
gdb_test "python print (1)" \
    "Python not initialized"
...
but apparently, that doesn't work anymore in this python version:
...
$ python --version
Python 3.15.0b4
...

The test-case needs updating, and I've submitted a testsuite patch [1] for
that.

The next question is why we're seeing a ModuleNotFoundError on quit.

I investigated this, and found that it originates from
gdbpy_initialize_gdb_readline, where we do:
...
   if (eval_python_command (code, Py_file_input) == 0)
    PyOS_ReadlineFunctionPointer = gdbpy_readline_wrapper;
...
but don't report and reset the python error state, so instead the error is
reported by Py_Finalize.

Fix this by:
- making sure that the error is reported immediately, though in the form of a
  warning rather than an error, and
- disabling the python-interactive command if gdbpy_initialize_gdb_readline
  fails, to avoid broken readline behavior in a python-interactive session.

Also make the test-case a bit stricter by checking that there's no output when
quitting.

Tested on aarch64-linux.

Approved-By: Tom Tromey <tom@tromey.com>
Changes in v2:
- use gdbpy_print_stack instead of PyErr_Print/PyErr_Clear
- Fix error/warning message by ensure that command name is double-quoted and
  displayed using command_style

Versions:
- v1 https://sourceware.org/pipermail/gdb-patches/2026-August/229261.html

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=34485

[1] https://sourceware.org/pipermail/gdb-patches/2026-August/229193.html

gdb/python/py-gdb-readline.c
gdb/python/python-internal.h
gdb/python/python.c
gdb/testsuite/gdb.python/py-failed-init.exp

index e8e2c23547cee673361405435e3a5cdd4c62344d..1bb83adcc42180060614c504d08a27ba3c32ac5a 100644 (file)
@@ -19,7 +19,9 @@
 
 #include "python-internal.h"
 #include "top.h"
+#include "ui-out.h"
 #include "cli/cli-utils.h"
+#include "cli/cli-style.h"
 
 /* Readline function suitable for PyOS_ReadlineFunctionPointer, which
    is used for Python's interactive parser and raw_input.  In both
@@ -116,8 +118,23 @@ sys.meta_path.insert(2, GdbRemoveReadlineFinder())\n\
 ";
   if (eval_python_command (code, Py_file_input) == 0)
     PyOS_ReadlineFunctionPointer = gdbpy_readline_wrapper;
+  else
+    {
+      gdbpy_print_stack ();
+
+      warning (_("Disabling import readline failed, \"%ps\" command disabled"),
+              styled_string (command_style.style (), "python-interactive"));
+    }
 
   return 0;
 }
 
+/* See python-internal.h.  */
+
+bool
+gdbpy_import_readline_disabled ()
+{
+  return PyOS_ReadlineFunctionPointer == gdbpy_readline_wrapper;
+}
+
 GDBPY_INITIALIZE_FILE (gdbpy_initialize_gdb_readline);
index 1165f165e29dbede776918d305d29816a50fef6a..3be1cc3ad5551901302c67d95b5a5492867dabe3 100644 (file)
@@ -1391,4 +1391,9 @@ py_notimplemented ()
 #include "py-wrappers.h"
 #include "py-safety.h"
 
+/* Return true if import readline was successfully disabled during
+   initialization.  */
+
+extern bool gdbpy_import_readline_disabled ();
+
 #endif /* GDB_PYTHON_PYTHON_INTERNAL_H */
index 7b5de98b90382c64765b0230346cede938c39339..0d6134ef0606b9ab68f88f46085b834ff31072a5 100644 (file)
@@ -383,6 +383,10 @@ eval_python_command (const char *command, int start_symbol,
 static void
 python_interactive_command (const char *arg, int from_tty)
 {
+  if (!gdbpy_import_readline_disabled ())
+    error (_("Disabling import readline failed, \"%ps\" command disabled"),
+          styled_string (command_style.style (), "python-interactive"));
+
   struct ui *ui = current_ui;
   int err;
 
index 622743d8d9bb7549e57d39c63a23ba4788bd6d1f..c2b9c990e297bc5830e7c19f8aad1e761cafd523 100644 (file)
@@ -24,8 +24,14 @@ save_vars { env(PYTHONHOME) } {
 gdb_test "python print (1)" \
     "Python not initialized"
 
+set output_seen 0
 gdb_test_multiple "quit" "" {
+    -re ^quit\r\n {
+       exp_continue
+    }
     eof {
+       set output_seen [expr [string length $expect_out(buffer)] != 0]
        pass $gdb_test_name
     }
 }
+gdb_assert {!$output_seen} "no output after quit"