* [PATCH v2 0/4] gdb/python: more fixes again for Python limited C API support
@ 2026-04-28 16:24 Matthieu Longo
2026-04-28 16:24 ` [PATCH v2 1/4] gdb/python: add gdbpy_borrowed_ref Matthieu Longo
` (4 more replies)
0 siblings, 5 replies; 23+ messages in thread
From: Matthieu Longo @ 2026-04-28 16:24 UTC (permalink / raw)
To: gdb-patches, Tom Tromey; +Cc: Matthieu Longo
This patch series fixes more issues encountered while enabling the Python limited C API in GDB. This is probably the last relatively-small patch series before the Python C extension types migration to the limited API.
Patch 1 reuses a previous patch publised by Tom Tromey [1], with additional modifications that are required for its usage in the Python C extension types migration.
Patch 2 adapts the signature of eval_python_command() to return both the exit code and the result.
Patch 3 migrates Python initialization to use the new config API (PEP 741) introduced by Python 3.14. Old code is preserved for older versions of Python.
Patch 4 proposes a workaround for the missing symbols not yet part of the limited API. Thanks to it, I was able to finish the compilation of GDB using the limited C API and run all the Python tests successfully.
All changes were tested by building GDB against the unlimited API of Python 3.10, 3.11, 3.12, 3.13 and 3.14, and the limited API of Python 3.14 (no build regression), and no regressions were observed in the testsuite.
Diff against v1 (https://inbox.sourceware.org/gdb-patches/20260409105155.1416274-1-matthieu.longo@arm.com/):
- eval_python_command: return pointer to result of evaluation.
- gdbpy_borrowed_ref and ref_ptr: add an implicit cast to bool.
- gdb_PyInitializer: replace throw_error() by error().
- init_done: use exception_print() to print the exception.
Regards,
Matthieu
[1]: https://inbox.sourceware.org/gdb-patches/20260222200759.1587070-2-tom@tromey.com/
Matthieu Longo (3):
gdb/python: eval_python_command returns result of the evaluation
gdb/python: migrate Python initialization to use the new config API (PEP 741)
gdb/python: work around missing symbols not yet part of Python limited API
Tom Tromey (1):
gdb/python: add gdbpy_borrowed_ref
gdb/python/py-gdb-readline.c | 2 +-
gdb/python/py-ref.h | 115 ++++++++++++++
gdb/python/python-internal.h | 7 +-
gdb/python/python-limited-api-missing.h | 62 ++++++++
gdb/python/python.c | 197 +++++++++++++++++++-----
gdb/varobj.c | 11 +-
gdbsupport/gdb_ref_ptr.h | 5 +
7 files changed, 346 insertions(+), 53 deletions(-)
create mode 100644 gdb/python/python-limited-api-missing.h
--
2.54.0
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v2 1/4] gdb/python: add gdbpy_borrowed_ref
2026-04-28 16:24 [PATCH v2 0/4] gdb/python: more fixes again for Python limited C API support Matthieu Longo
@ 2026-04-28 16:24 ` Matthieu Longo
2026-05-15 16:56 ` Tom Tromey
2026-04-28 16:24 ` [PATCH v2 2/4] gdb/python: eval_python_command returns result of the evaluation Matthieu Longo
` (3 subsequent siblings)
4 siblings, 1 reply; 23+ messages in thread
From: Matthieu Longo @ 2026-04-28 16:24 UTC (permalink / raw)
To: gdb-patches, Tom Tromey
From: Tom Tromey <tom@tromey.com>
This adds a new gdbpy_borrowed_ref class. This class is primarily for
code "documentation" purposes -- it makes it clear to the reader that
a given reference is borrowed. However, it also adds a tiny bit of
safety, in that conversion to gdbpy_ref<> will acquire a new
reference.
---
gdb/python/py-ref.h | 115 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 115 insertions(+)
diff --git a/gdb/python/py-ref.h b/gdb/python/py-ref.h
index dc0b14814af..8190dc38627 100644
--- a/gdb/python/py-ref.h
+++ b/gdb/python/py-ref.h
@@ -41,6 +41,121 @@ struct gdbpy_ref_policy
template<typename T = PyObject> using gdbpy_ref
= gdb::ref_ptr<T, gdbpy_ref_policy>;
+/* A class representing a borrowed reference.
+
+ This is a simple wrapper for a PyObject*. Aside from documenting
+ what the code does, the main advantage of using this is that
+ conversion to a gdbpy_ref<> is guaranteed to make a new
+ reference. */
+template <class T = PyObject>
+class gdbpy_borrowed_ref
+{
+public:
+
+ gdbpy_borrowed_ref () noexcept
+ : m_obj (nullptr)
+ {
+ }
+
+ gdbpy_borrowed_ref (const std::nullptr_t) noexcept
+ : m_obj (nullptr)
+ {
+ }
+
+ template <typename U,
+ typename = std::is_convertible<U *, T*>>
+ gdbpy_borrowed_ref (U *obj) noexcept
+ : m_obj (obj)
+ {
+ }
+
+ template <typename U,
+ typename = std::is_convertible<U *, T*>>
+ gdbpy_borrowed_ref (const gdbpy_ref<U> &ref) noexcept
+ : m_obj (ref.get ())
+ {
+ }
+
+ gdbpy_borrowed_ref (const gdbpy_borrowed_ref &other) noexcept
+ : m_obj (other.m_obj)
+ {
+ }
+
+ gdbpy_borrowed_ref &operator= (const gdbpy_borrowed_ref &other)
+ {
+ m_obj = other.m_obj;
+ return *this;
+ }
+
+ operator bool () const noexcept
+ {
+ return m_obj != nullptr;
+ }
+
+ operator T * () const noexcept
+ {
+ return m_obj;
+ }
+
+ /* Explicit version of the previous implicit cast operator. */
+ T *get () const noexcept
+ {
+ return m_obj;
+ }
+
+ /* When converting a borrowed reference to a gdbpy_ref<>, a new
+ reference is acquired. */
+ template <typename U,
+ typename = std::is_convertible<U *, T *>>
+ operator gdbpy_ref<U> ()
+ {
+ if (m_obj != nullptr)
+ return gdbpy_ref<U>::new_reference (m_obj);
+ return nullptr;
+ }
+
+ /* Convert a borrowed reference to a strong one, i.e. gdbpy_ref<>. */
+ gdbpy_ref<T> strong_ref () const noexcept
+ {
+ if (m_obj != nullptr)
+ return gdbpy_ref<T>::new_reference (m_obj);
+ return nullptr;
+ }
+
+ /* Let users refer to members of the underlying pointer. */
+ T *operator-> () const noexcept
+ {
+ return m_obj;
+ }
+
+private:
+ T *m_obj;
+};
+
+template<typename T>
+inline bool operator== (const gdbpy_borrowed_ref<T> &lhs, const std::nullptr_t)
+{
+ return lhs.get () == nullptr;
+}
+
+template<typename T>
+inline bool operator!= (const gdbpy_borrowed_ref<T> &lhs, const std::nullptr_t)
+{
+ return lhs.get () != nullptr;
+}
+
+template<typename T>
+inline bool operator== (const std::nullptr_t, const gdbpy_borrowed_ref<T> &rhs)
+{
+ return nullptr == rhs.get ();
+}
+
+template<typename T>
+inline bool operator!= (const std::nullptr_t, const gdbpy_borrowed_ref<T> &rhs)
+{
+ return nullptr != rhs.get ();
+}
+
/* A wrapper class for Python extension objects that have a __dict__ attribute.
Any Python C object extension needing __dict__ should inherit from this
--
2.54.0
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v2 2/4] gdb/python: eval_python_command returns result of the evaluation
2026-04-28 16:24 [PATCH v2 0/4] gdb/python: more fixes again for Python limited C API support Matthieu Longo
2026-04-28 16:24 ` [PATCH v2 1/4] gdb/python: add gdbpy_borrowed_ref Matthieu Longo
@ 2026-04-28 16:24 ` Matthieu Longo
2026-05-15 16:51 ` Tom Tromey
2026-04-28 16:24 ` [PATCH v2 3/4] gdb/python: migrate Python initialization to use the new config API (PEP 741) Matthieu Longo
` (2 subsequent siblings)
4 siblings, 1 reply; 23+ messages in thread
From: Matthieu Longo @ 2026-04-28 16:24 UTC (permalink / raw)
To: gdb-patches, Tom Tromey; +Cc: Matthieu Longo
A previous commit [1] centralized the Python code evaluation in the
helper function eval_python_command(). However, a missed case in
varobj_set_visualizer() requires not only the exit status but the
result of the evaluation too.
This patch updates eval_python_command() to return the result of
the evaluation, or NULL if an error occurred. It also adjusts the
existing callers accordingly. Finally, it replaces the use of
PyRun_String() in varobj_set_visualizer() with a call to
eval_python_command().
[1]: 264a8a2236e8aa64b333a69e42a55ff8c0844f6e
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=23830
---
gdb/python/py-gdb-readline.c | 2 +-
gdb/python/python-internal.h | 4 +-
gdb/python/python.c | 75 +++++++++++++++++-------------------
gdb/varobj.c | 11 +-----
gdbsupport/gdb_ref_ptr.h | 5 +++
5 files changed, 45 insertions(+), 52 deletions(-)
diff --git a/gdb/python/py-gdb-readline.c b/gdb/python/py-gdb-readline.c
index e8e2c23547c..b7f8caeccd4 100644
--- a/gdb/python/py-gdb-readline.c
+++ b/gdb/python/py-gdb-readline.c
@@ -114,7 +114,7 @@ class GdbRemoveReadlineFinder(MetaPathFinder):\n\
\n\
sys.meta_path.insert(2, GdbRemoveReadlineFinder())\n\
";
- if (eval_python_command (code, Py_file_input) == 0)
+ if (eval_python_command (code, Py_file_input))
PyOS_ReadlineFunctionPointer = gdbpy_readline_wrapper;
return 0;
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 37bc37691fe..577260c4066 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -1337,7 +1337,7 @@ class gdbpy_memoizing_registry_storage
gdb::unordered_map<val_type *, obj_type *> m_objects;
};
-extern int eval_python_command (const char *command, int start_symbol,
- const char *filename = nullptr);
+extern gdbpy_ref<> eval_python_command
+ (const char *command, int start_symbol, const char *filename = nullptr);
#endif /* GDB_PYTHON_PYTHON_INTERNAL_H */
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 502fe682b2c..546d1272007 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -297,44 +297,43 @@ gdbpy_check_quit_flag (const struct extension_language_defn *extlang)
Python start symbol, and does not automatically print the stack on
errors. FILENAME is used to set the file name in error messages;
NULL means that this is evaluating a string, not the contents of a
- file. */
+ file.
+ Return the result of the evaluation on success, NULL on failure. */
-int
+gdbpy_ref<>
eval_python_command (const char *command, int start_symbol,
const char *filename)
{
- PyObject *m, *d;
-
- m = PyImport_AddModule ("__main__");
- if (m == NULL)
- return -1;
+ gdbpy_borrowed_ref<> mainmod = PyImport_AddModule ("__main__");
+ if (mainmod == nullptr)
+ return nullptr;
- d = PyModule_GetDict (m);
- if (d == NULL)
- return -1;
+ gdbpy_borrowed_ref<> globals = PyModule_GetDict (mainmod);
+ if (globals == nullptr)
+ return nullptr;
bool file_set = false;
if (filename != nullptr)
{
gdbpy_ref<> file = host_string_to_python_string ("__file__");
if (file == nullptr)
- return -1;
+ return nullptr;
/* PyDict_GetItemWithError returns a borrowed reference. */
- PyObject *found = PyDict_GetItemWithError (d, file.get ());
+ PyObject *found = PyDict_GetItemWithError (globals, file.get ());
if (found == nullptr)
{
if (PyErr_Occurred ())
- return -1;
+ return nullptr;
gdbpy_ref<> filename_obj = host_string_to_python_string (filename);
if (filename_obj == nullptr)
- return -1;
+ return nullptr;
- if (PyDict_SetItem (d, file.get (), filename_obj.get ()) < 0)
- return -1;
- if (PyDict_SetItemString (d, "__cached__", Py_None) < 0)
- return -1;
+ if (PyDict_SetItem (globals, file.get (), filename_obj.get ()) < 0)
+ return nullptr;
+ if (PyDict_SetItemString (globals, "__cached__", Py_None) < 0)
+ return nullptr;
file_set = true;
}
@@ -347,12 +346,12 @@ eval_python_command (const char *command, int start_symbol,
: filename,
start_symbol));
- int result = -1;
+ gdbpy_ref<> eval_result;
if (code != nullptr)
{
- gdbpy_ref<> eval_result (PyEval_EvalCode (code.get (), d, d));
- if (eval_result != nullptr)
- result = 0;
+ eval_result.reset (PyEval_EvalCode (code.get (), globals, globals));
+ if (eval_result == nullptr)
+ gdb_assert (PyErr_Occurred ());
}
if (file_set)
@@ -360,21 +359,21 @@ eval_python_command (const char *command, int start_symbol,
/* If there's already an exception occurring, preserve it and
restore it before returning from this function. */
std::optional<gdbpy_err_fetch> save_error;
- if (result < 0)
+ if (eval_result == nullptr)
save_error.emplace ();
/* CPython also just ignores errors here. These should be
expected to be exceedingly rare anyway. */
- if (PyDict_DelItemString (d, "__file__") < 0)
+ if (PyDict_DelItemString (globals, "__file__") < 0)
PyErr_Clear ();
- if (PyDict_DelItemString (d, "__cached__") < 0)
+ if (PyDict_DelItemString (globals, "__cached__") < 0)
PyErr_Clear ();
if (save_error.has_value ())
save_error->restore ();
}
- return result;
+ return eval_result;
}
/* Implementation of the gdb "python-interactive" command. */
@@ -383,7 +382,7 @@ static void
python_interactive_command (const char *arg, int from_tty)
{
struct ui *ui = current_ui;
- int err;
+ bool err;
scoped_restore save_async = make_scoped_restore (¤t_ui->async, 0);
@@ -395,11 +394,11 @@ python_interactive_command (const char *arg, int from_tty)
{
std::string script = std::string (arg) + "\n";
/* Py_single_input causes the result to be displayed. */
- err = eval_python_command (script.c_str (), Py_single_input);
+ err = ! eval_python_command (script.c_str (), Py_single_input);
}
else
{
- err = PyRun_InteractiveLoop (ui->instream, "<stdin>");
+ err = PyRun_InteractiveLoop (ui->instream, "<stdin>") != 0;
dont_repeat ();
}
@@ -410,6 +409,7 @@ python_interactive_command (const char *arg, int from_tty)
/* Like PyRun_SimpleFile, but if there is an exception, it is not
automatically displayed. FILE is the Python script to run named
FILENAME.
+ Return true on success, false on failure.
On Windows hosts few users would build Python themselves (this is no
trivial task on this platform), and thus use binaries built by
@@ -420,7 +420,7 @@ python_interactive_command (const char *arg, int from_tty)
A FILE * from one runtime does not necessarily operate correctly in
the other runtime. */
-static int
+static bool
python_run_simple_file (FILE *file, const char *filename)
{
std::string contents = read_remainder_of_file (file);
@@ -458,8 +458,7 @@ gdbpy_eval_from_control_command (const struct extension_language_defn *extlang,
gdbpy_enter enter_py;
std::string script = compute_python_string (cmd->body_list_0.get ());
- int ret = eval_python_command (script.c_str (), Py_file_input);
- if (ret != 0)
+ if (! eval_python_command (script.c_str (), Py_file_input))
gdbpy_handle_exception ();
}
@@ -475,8 +474,7 @@ python_command (const char *arg, int from_tty)
arg = skip_spaces (arg);
if (arg && *arg)
{
- int ret = eval_python_command (arg, Py_file_input);
- if (ret != 0)
+ if (! eval_python_command (arg, Py_file_input))
gdbpy_handle_exception ();
}
else
@@ -1119,8 +1117,7 @@ gdbpy_source_script (const struct extension_language_defn *extlang,
FILE *file, const char *filename)
{
gdbpy_enter enter_py;
- int result = python_run_simple_file (file, filename);
- if (result != 0)
+ if (! python_run_simple_file (file, filename))
gdbpy_handle_exception ();
}
@@ -1826,8 +1823,7 @@ gdbpy_source_objfile_script (const struct extension_language_defn *extlang,
scoped_restore restire_current_objfile
= make_scoped_restore (&gdbpy_current_objfile, objfile);
- int result = python_run_simple_file (file, filename);
- if (result != 0)
+ if (! python_run_simple_file (file, filename))
gdbpy_print_stack ();
}
@@ -1849,8 +1845,7 @@ gdbpy_execute_objfile_script (const struct extension_language_defn *extlang,
scoped_restore restire_current_objfile
= make_scoped_restore (&gdbpy_current_objfile, objfile);
- int ret = eval_python_command (script, Py_file_input);
- if (ret != 0)
+ if (! eval_python_command (script, Py_file_input))
gdbpy_print_stack ();
}
diff --git a/gdb/varobj.c b/gdb/varobj.c
index edd94ea4963..a8c991e50eb 100644
--- a/gdb/varobj.c
+++ b/gdb/varobj.c
@@ -1365,20 +1365,13 @@ void
varobj_set_visualizer (struct varobj *var, const char *visualizer)
{
#if HAVE_PYTHON
- PyObject *mainmod;
-
if (!gdb_python_initialized)
return;
gdbpy_enter_varobj enter_py (var);
- mainmod = PyImport_AddModule ("__main__");
- gdbpy_ref<> globals
- = gdbpy_ref<>::new_reference (PyModule_GetDict (mainmod));
- gdbpy_ref<> constructor (PyRun_String (visualizer, Py_eval_input,
- globals.get (), globals.get ()));
-
- if (constructor == NULL)
+ auto constructor = eval_python_command (visualizer, Py_eval_input);
+ if (constructor == nullptr)
{
gdbpy_print_stack ();
error (_("Could not evaluate visualizer expression: %s"), visualizer);
diff --git a/gdbsupport/gdb_ref_ptr.h b/gdbsupport/gdb_ref_ptr.h
index 0eb654324c6..f7c7c0afac6 100644
--- a/gdbsupport/gdb_ref_ptr.h
+++ b/gdbsupport/gdb_ref_ptr.h
@@ -179,6 +179,11 @@ class ref_ptr
return m_obj;
}
+ operator bool () const noexcept
+ {
+ return m_obj != nullptr;
+ }
+
/* Return this instance's referent, and stop managing this
reference. The caller is now responsible for the ownership of
the reference. */
--
2.54.0
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v2 3/4] gdb/python: migrate Python initialization to use the new config API (PEP 741)
2026-04-28 16:24 [PATCH v2 0/4] gdb/python: more fixes again for Python limited C API support Matthieu Longo
2026-04-28 16:24 ` [PATCH v2 1/4] gdb/python: add gdbpy_borrowed_ref Matthieu Longo
2026-04-28 16:24 ` [PATCH v2 2/4] gdb/python: eval_python_command returns result of the evaluation Matthieu Longo
@ 2026-04-28 16:24 ` Matthieu Longo
2026-05-14 19:25 ` Tom Tromey
2026-04-28 16:24 ` [PATCH v2 4/4] gdb/python: work around missing symbols not yet part of Python limited API Matthieu Longo
2026-05-14 10:23 ` [PATCH v2 0/4] gdb/python: more fixes again for Python limited C API support Matthieu Longo
4 siblings, 1 reply; 23+ messages in thread
From: Matthieu Longo @ 2026-04-28 16:24 UTC (permalink / raw)
To: gdb-patches, Tom Tromey; +Cc: Matthieu Longo
GDB currently initializes CPython using the PyConfig C API introduced in
Python 3.8 (PEP 597). From an ABI stability perspective, this API has a
major drawback: it exposes a plain structure whose fields may be added or
removed between Python versions. As a result, it was excluded from the
Python limited API.
Python 3.14 introduced a new configuration API (PEP 741) that avoids
exposing plain structures and instead, operates via opaque pointers.
This design makes it much more suitable for inclusion in the Python
limited API. Indeed, this was the original intent of the PEP-741 author.
However, CPython maintainers ultimately decided otherwise.
Since GDB aims at using the CPython stable API to avoid rebuilding for
each Python version, the absence of a configuration API in the limited
C API constitutes a blocker. Nevertheless, this can be worked around by
using PEP-741 configuration API, whose design is compatible with the
limited C API. It is relatively safe to assume that this API will stay
around for some time.
In this perspective, this patch adds support for using the PEP-741 config
API starting from Python 3.14. When Py_LIMITED_API is defined, the
required functions are exposed as external symbols via a workaround header.
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=23830
---
gdb/python/python.c | 122 +++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 121 insertions(+), 1 deletion(-)
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 546d1272007..10a77880340 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -22,6 +22,7 @@
#include "ui-out.h"
#include "cli/cli-script.h"
#include "cli/cli-cmds.h"
+#include "exceptions.h"
#include "progspace.h"
#include "objfiles.h"
#include "value.h"
@@ -2210,6 +2211,93 @@ static bool python_dont_write_bytecode_at_python_initialization;
to passing `-E' to the python program. */
static bool python_ignore_environment = false;
+#if PY_VERSION_HEX >= 0x030e0000
+namespace {
+
+/* A class wrapper around the new configuration functions introduced by
+ PEP 741 in Python 3.14. */
+class gdb_PyInitializer
+{
+public:
+ gdb_PyInitializer ();
+ ~gdb_PyInitializer ();
+
+ void set_opt (const char *opt_name, bool value);
+ void set_opt (const char *opt_name, const char *value);
+
+ /* Initialize Python with the current config.
+ Note: this function should be called before the object goes out of scope
+ or an assertion will be triggered in the destructor at runtime. Setting
+ options value after this call does not have any effect. */
+ void initialize ();
+
+private:
+ [[noreturn]] void handle_error ();
+
+ PyInitConfig *m_config;
+ bool m_err = false;
+ bool m_py_initialized = false;
+};
+
+gdb_PyInitializer::gdb_PyInitializer ()
+ : m_config (PyInitConfig_Create ())
+{
+ if (m_config == nullptr)
+ error (_("Python initialization failed: memory allocation failed"));
+}
+
+gdb_PyInitializer::~gdb_PyInitializer ()
+{
+ /* If the condition below is false, the calling context probably forgot to
+ call initialize(). */
+ gdb_assert (m_err || m_py_initialized);
+
+ if (m_config)
+ PyInitConfig_Free (m_config);
+}
+
+[[noreturn]] void
+gdb_PyInitializer::handle_error ()
+{
+ m_err = true;
+
+ const char *err_msg;
+ if (PyInitConfig_GetError (m_config, &err_msg) == 1)
+ error (_("Python initialization failed: %s"), err_msg);
+
+ int exit_code;
+ if (PyInitConfig_GetExitCode (m_config, &exit_code) != 0)
+ error (_("Python initialization failed with exit status: %d"), exit_code);
+ else
+ error (_("Python initialization failed"));
+}
+
+void
+gdb_PyInitializer::set_opt (const char *opt_name, bool value)
+{
+ if (PyInitConfig_SetInt (m_config, opt_name, value) < 0)
+ handle_error ();
+}
+
+void
+gdb_PyInitializer::set_opt (const char *opt_name, const char *value)
+{
+ if (PyInitConfig_SetStr (m_config, opt_name, value) < 0)
+ handle_error ();
+}
+
+void
+gdb_PyInitializer::initialize ()
+{
+ if (!m_py_initialized && Py_InitializeFromInitConfig (m_config) < 0)
+ handle_error ();
+ m_py_initialized = true;
+}
+
+} /* namespace anonymous */
+
+#endif /* PY_VERSION_HEX >= 0x030e0000 */
+
/* Implement 'show python ignore-environment'. */
static void
@@ -2543,7 +2631,8 @@ py_initialize ()
Py_IgnoreEnvironmentFlag
= python_ignore_environment_at_python_initialization ? 1 : 0;
return py_initialize_catch_abort ();
-#else
+
+#elif PY_VERSION_HEX < 0x030e0000
PyConfig config;
PyConfig_InitPythonConfig (&config);
@@ -2579,6 +2668,37 @@ py_initialize ()
py_isinitialized = true;
return true;
+
+#else
+ try
+ {
+ gdb_PyInitializer py_config;
+
+ py_config.set_opt ("program_name", progname.get ());
+ py_config.set_opt ("write_bytecode",
+ !python_dont_write_bytecode_at_python_initialization);
+ py_config.set_opt ("isolated", false);
+ py_config.set_opt ("configure_locale", true);
+ py_config.set_opt ("pathconfig_warnings", true);
+ py_config.set_opt ("parse_argv", true);
+ py_config.set_opt ("safe_path", false);
+ py_config.set_opt ("configure_c_stdio", true);
+ py_config.set_opt ("stdio_encoding", "utf-8");
+ py_config.set_opt ("stdio_errors", "strict");
+ py_config.set_opt ("install_signal_handlers", true);
+ py_config.set_opt ("use_environment",
+ !python_ignore_environment_at_python_initialization);
+ py_config.set_opt ("user_site_directory", true);
+
+ py_config.initialize ();
+ py_isinitialized = true;
+ }
+ catch (const gdb_exception_error &exc)
+ {
+ exception_print (gdb_stderr, exc);
+ py_isinitialized = false;
+ }
+ return py_isinitialized;
#endif
}
--
2.54.0
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v2 4/4] gdb/python: work around missing symbols not yet part of Python limited API
2026-04-28 16:24 [PATCH v2 0/4] gdb/python: more fixes again for Python limited C API support Matthieu Longo
` (2 preceding siblings ...)
2026-04-28 16:24 ` [PATCH v2 3/4] gdb/python: migrate Python initialization to use the new config API (PEP 741) Matthieu Longo
@ 2026-04-28 16:24 ` Matthieu Longo
2026-05-14 19:26 ` Tom Tromey
2026-05-14 10:23 ` [PATCH v2 0/4] gdb/python: more fixes again for Python limited C API support Matthieu Longo
4 siblings, 1 reply; 23+ messages in thread
From: Matthieu Longo @ 2026-04-28 16:24 UTC (permalink / raw)
To: gdb-patches, Tom Tromey; +Cc: Matthieu Longo
Most Python API usages in GDB can be migrated to the limited API, except
the following:
- PEP-741's configuration structures and functions, which use opaque
types. They were originally intended to be part of the Python limited
API, but some Python core maintainers opposed their inclusion at the
time.
- PyOS_ReadlineFunctionPointer, a global variable storing a function
used to override PyOS_StdioReadline(). The signature has remained
unchanged for a long time.
- PyRun_InteractiveLoop, used to read and execute Python statements when
embedding an interactive interpreter. Its signature has also remained
stable for a long time.
Since no limited API alternatives exist for these, and given their long
history of ABI stability, one approach is to expose them in a GDB header
and rely on their continued stability. While this is not without risk,
it seems acceptable given the arguments above. This would remove the
remaining obstacles preventing GDB from being agnostic to the Python
version available at runtime.
That said, issues should be opened on CPython issue tracker to request
that these functions be included in the limited API in future versions.
Last but not least, GDB does not need to officially support the Python
limited API. The '--enable-py-limited-api' option can remain experimental,
with appropriate forewarnings about its limitations and guarantees.
This patch adds a new header, python-limited-api-missing.h, which
exposes symbols not yet part of the Python limited API.
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=23830
---
gdb/python/python-internal.h | 3 ++
gdb/python/python-limited-api-missing.h | 62 +++++++++++++++++++++++++
2 files changed, 65 insertions(+)
create mode 100644 gdb/python/python-limited-api-missing.h
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 577260c4066..3537705d60d 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -57,6 +57,9 @@
double quotes. On case-insensitive filesystems, this prevents us
from including our python/python.h header file. */
#include <Python.h>
+#ifdef Py_LIMITED_API
+#include "python-limited-api-missing.h"
+#endif
#include <frameobject.h>
#include "py-ref.h"
#include "py-obj-type.h"
diff --git a/gdb/python/python-limited-api-missing.h b/gdb/python/python-limited-api-missing.h
new file mode 100644
index 00000000000..07882ca1cf2
--- /dev/null
+++ b/gdb/python/python-limited-api-missing.h
@@ -0,0 +1,62 @@
+/* Gdb/Python header exposing missing symbols in the Python limited API.
+ Note: this is a workaround solution until those existing symbols below,
+ or new symbols are exposed in the limited API.
+
+ Copyright (C) 2026 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef GDB_PYTHON_LIMITED_API_MISSING_H
+#define GDB_PYTHON_LIMITED_API_MISSING_H
+
+#ifdef Py_LIMITED_API
+extern "C"
+{
+
+/* Symbols belonging to the configuration API introduced in PEP-741, and
+ required in gdb_PyInitializer. */
+
+typedef struct PyInitConfig PyInitConfig;
+
+PyAPI_FUNC(PyInitConfig*) PyInitConfig_Create (void);
+PyAPI_FUNC(void) PyInitConfig_Free (PyInitConfig *config);
+
+PyAPI_FUNC(int) PyInitConfig_SetInt (PyInitConfig *config,
+ const char *name,
+ int64_t value);
+PyAPI_FUNC(int) PyInitConfig_SetStr (PyInitConfig *config,
+ const char *name,
+ const char *value);
+
+PyAPI_FUNC(int) PyInitConfig_GetError (PyInitConfig* config,
+ const char **err_msg);
+PyAPI_FUNC(int) PyInitConfig_GetExitCode (PyInitConfig* config,
+ int *exitcode);
+
+PyAPI_FUNC(int) Py_InitializeFromInitConfig (PyInitConfig *config);
+
+/* Handler for GDB's readline support. */
+
+PyAPI_DATA(char) *(*PyOS_ReadlineFunctionPointer)(FILE *, FILE *, const char *);
+
+/* Utils from Python's high level layer API. */
+
+PyAPI_FUNC(int) PyRun_InteractiveLoop (FILE *f, const char *p);
+
+}
+#endif /* Py_LIMITED_API */
+
+#endif /* GDB_PYTHON_LIMITED_API_MISSING_H */
--
2.54.0
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 0/4] gdb/python: more fixes again for Python limited C API support
2026-04-28 16:24 [PATCH v2 0/4] gdb/python: more fixes again for Python limited C API support Matthieu Longo
` (3 preceding siblings ...)
2026-04-28 16:24 ` [PATCH v2 4/4] gdb/python: work around missing symbols not yet part of Python limited API Matthieu Longo
@ 2026-05-14 10:23 ` Matthieu Longo
2026-05-14 19:27 ` Tom Tromey
4 siblings, 1 reply; 23+ messages in thread
From: Matthieu Longo @ 2026-05-14 10:23 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
On 28/04/2026 17:24, Matthieu Longo wrote:
> This patch series fixes more issues encountered while enabling the Python limited C API in GDB. This is probably the last relatively-small patch series before the Python C extension types migration to the limited API.
>
> Patch 1 reuses a previous patch publised by Tom Tromey [1], with additional modifications that are required for its usage in the Python C extension types migration.
> Patch 2 adapts the signature of eval_python_command() to return both the exit code and the result.
> Patch 3 migrates Python initialization to use the new config API (PEP 741) introduced by Python 3.14. Old code is preserved for older versions of Python.
> Patch 4 proposes a workaround for the missing symbols not yet part of the limited API. Thanks to it, I was able to finish the compilation of GDB using the limited C API and run all the Python tests successfully.
>
> All changes were tested by building GDB against the unlimited API of Python 3.10, 3.11, 3.12, 3.13 and 3.14, and the limited API of Python 3.14 (no build regression), and no regressions were observed in the testsuite.
>
> Diff against v1 (https://inbox.sourceware.org/gdb-patches/20260409105155.1416274-1-matthieu.longo@arm.com/):
> - eval_python_command: return pointer to result of evaluation.
> - gdbpy_borrowed_ref and ref_ptr: add an implicit cast to bool.
> - gdb_PyInitializer: replace throw_error() by error().
> - init_done: use exception_print() to print the exception.
>
> Regards,
> Matthieu
>
> [1]: https://inbox.sourceware.org/gdb-patches/20260222200759.1587070-2-tom@tromey.com/
>
> Matthieu Longo (3):
> gdb/python: eval_python_command returns result of the evaluation
> gdb/python: migrate Python initialization to use the new config API (PEP 741)
> gdb/python: work around missing symbols not yet part of Python limited API
>
> Tom Tromey (1):
> gdb/python: add gdbpy_borrowed_ref
>
> gdb/python/py-gdb-readline.c | 2 +-
> gdb/python/py-ref.h | 115 ++++++++++++++
> gdb/python/python-internal.h | 7 +-
> gdb/python/python-limited-api-missing.h | 62 ++++++++
> gdb/python/python.c | 197 +++++++++++++++++++-----
> gdb/varobj.c | 11 +-
> gdbsupport/gdb_ref_ptr.h | 5 +
> 7 files changed, 346 insertions(+), 53 deletions(-)
> create mode 100644 gdb/python/python-limited-api-missing.h
>
Hi Tom,
Do you know when you will be able to start having a look at this patch series ?
Matthieu
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 3/4] gdb/python: migrate Python initialization to use the new config API (PEP 741)
2026-04-28 16:24 ` [PATCH v2 3/4] gdb/python: migrate Python initialization to use the new config API (PEP 741) Matthieu Longo
@ 2026-05-14 19:25 ` Tom Tromey
0 siblings, 0 replies; 23+ messages in thread
From: Tom Tromey @ 2026-05-14 19:25 UTC (permalink / raw)
To: Matthieu Longo; +Cc: gdb-patches, Tom Tromey
>>>>> Matthieu Longo <matthieu.longo@arm.com> writes:
> GDB currently initializes CPython using the PyConfig C API introduced in
> Python 3.8 (PEP 597). From an ABI stability perspective, this API has a
> major drawback: it exposes a plain structure whose fields may be added or
> removed between Python versions. As a result, it was excluded from the
> Python limited API.
Thanks for the patch.
A couple nits below but ok with those fixed.
Approved-By: Tom Tromey <tom@tromey.com>
> +#if PY_VERSION_HEX >= 0x030e0000
> +namespace {
I think a blank line between these lines would be good.
> + try
> + {
I think the "{" needs two more spaces of indentation, likewise the body.
> + catch (const gdb_exception_error &exc)
> + {
Here too.
Tom
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 4/4] gdb/python: work around missing symbols not yet part of Python limited API
2026-04-28 16:24 ` [PATCH v2 4/4] gdb/python: work around missing symbols not yet part of Python limited API Matthieu Longo
@ 2026-05-14 19:26 ` Tom Tromey
2026-05-15 10:10 ` Matthieu Longo
0 siblings, 1 reply; 23+ messages in thread
From: Tom Tromey @ 2026-05-14 19:26 UTC (permalink / raw)
To: Matthieu Longo; +Cc: gdb-patches, Tom Tromey
>>>>> Matthieu Longo <matthieu.longo@arm.com> writes:
> This patch adds a new header, python-limited-api-missing.h, which
> exposes symbols not yet part of the Python limited API.
This is ok.
Approved-By: Tom Tromey <tom@tromey.com>
Tom
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 0/4] gdb/python: more fixes again for Python limited C API support
2026-05-14 10:23 ` [PATCH v2 0/4] gdb/python: more fixes again for Python limited C API support Matthieu Longo
@ 2026-05-14 19:27 ` Tom Tromey
2026-05-15 9:26 ` Matthieu Longo
0 siblings, 1 reply; 23+ messages in thread
From: Tom Tromey @ 2026-05-14 19:27 UTC (permalink / raw)
To: Matthieu Longo; +Cc: Tom Tromey, gdb-patches
Matthieu> Do you know when you will be able to start having a look at this patch series ?
I sent some notes a while ago IIRC, but I ok'd the last two just now.
thanks,
Tom
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 0/4] gdb/python: more fixes again for Python limited C API support
2026-05-14 19:27 ` Tom Tromey
@ 2026-05-15 9:26 ` Matthieu Longo
2026-05-15 14:48 ` Tom Tromey
0 siblings, 1 reply; 23+ messages in thread
From: Matthieu Longo @ 2026-05-15 9:26 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
On 14/05/2026 20:27, Tom Tromey wrote:
> Matthieu> Do you know when you will be able to start having a look at this patch series ?
>
> I sent some notes a while ago IIRC, but I ok'd the last two just now.
>
> thanks,
> Tom
Notes on v2 ?
I checked on https://inbox.sourceware.org/gdb-patches, and I cannot see any other messages except
the two from yesterday on patch 3 and 4.
Same on v1, I replied to all your messages.
Please could you double check on your side whether your messages were actually sent ?
Regards,
Matthieu
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 4/4] gdb/python: work around missing symbols not yet part of Python limited API
2026-05-14 19:26 ` Tom Tromey
@ 2026-05-15 10:10 ` Matthieu Longo
0 siblings, 0 replies; 23+ messages in thread
From: Matthieu Longo @ 2026-05-15 10:10 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
On 14/05/2026 20:26, Tom Tromey wrote:
>>>>>> Matthieu Longo <matthieu.longo@arm.com> writes:
>
>> This patch adds a new header, python-limited-api-missing.h, which
>> exposes symbols not yet part of the Python limited API.
>
> This is ok.
> Approved-By: Tom Tromey <tom@tromey.com>
>
> Tom
I noticed that I had forgotten to add the headers in Makefile.in
I amended the patch as follows and merged:
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index a0c11f5eb57..b2b7ef3d606 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1616,6 +1616,7 @@ HFILES_NO_SRCDIR = \
python/py-stopevent.h \
python/python.h \
python/python-internal.h \
+ python/python-limited-api-missing.h \
python/py-uiout.h \
quick-symbol.h \
ravenscar-thread.h \
Matthieu
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 0/4] gdb/python: more fixes again for Python limited C API support
2026-05-15 9:26 ` Matthieu Longo
@ 2026-05-15 14:48 ` Tom Tromey
0 siblings, 0 replies; 23+ messages in thread
From: Tom Tromey @ 2026-05-15 14:48 UTC (permalink / raw)
To: Matthieu Longo; +Cc: Tom Tromey, gdb-patches
> Notes on v2 ?
> I checked on https://inbox.sourceware.org/gdb-patches, and I cannot
> see any other messages except the two from yesterday on patch 3 and 4.
> Same on v1, I replied to all your messages.
> Please could you double check on your side whether your messages were actually sent ?
Yeah, I don't see them either.
I went and re-read the patches though and I distinctly remember writing
the replies. Weird. Anyway I'll rewrite them.
Tom
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 2/4] gdb/python: eval_python_command returns result of the evaluation
2026-04-28 16:24 ` [PATCH v2 2/4] gdb/python: eval_python_command returns result of the evaluation Matthieu Longo
@ 2026-05-15 16:51 ` Tom Tromey
2026-05-18 14:37 ` Matthieu Longo
0 siblings, 1 reply; 23+ messages in thread
From: Tom Tromey @ 2026-05-15 16:51 UTC (permalink / raw)
To: Matthieu Longo; +Cc: gdb-patches, Tom Tromey
>>>>> Matthieu Longo <matthieu.longo@arm.com> writes:
> +++ b/gdbsupport/gdb_ref_ptr.h
> @@ -179,6 +179,11 @@ class ref_ptr
> return m_obj;
> }
> + operator bool () const noexcept
> + {
> + return m_obj != nullptr;
> + }
gdb is normally explicit instead, and since there aren't many call sites
I think this operator should be dropped and those spots updated to check
against nullptr.
Tom
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 1/4] gdb/python: add gdbpy_borrowed_ref
2026-04-28 16:24 ` [PATCH v2 1/4] gdb/python: add gdbpy_borrowed_ref Matthieu Longo
@ 2026-05-15 16:56 ` Tom Tromey
2026-05-18 14:25 ` Matthieu Longo
0 siblings, 1 reply; 23+ messages in thread
From: Tom Tromey @ 2026-05-15 16:56 UTC (permalink / raw)
To: Matthieu Longo; +Cc: gdb-patches, Tom Tromey
>>>>> Matthieu Longo <matthieu.longo@arm.com> writes:
> From: Tom Tromey <tom@tromey.com>
> This adds a new gdbpy_borrowed_ref class. This class is primarily for
> code "documentation" purposes -- it makes it clear to the reader that
> a given reference is borrowed. However, it also adds a tiny bit of
> safety, in that conversion to gdbpy_ref<> will acquire a new
> reference.
> +template <class T = PyObject>
> +class gdbpy_borrowed_ref
> +{
On my "safety" branch I ended up rewriting this to have a base class
that allows NULL and a derived class that does not.
And my initial review -- the one that didn't get sent -- suggested using
this.
But I tend to think this patch isn't needed yet. It's nice for the
safety series but in this series it is only used a little in patch 2,
and not even every spot that could/should use it -- like I noticed:
/* PyDict_GetItemWithError returns a borrowed reference. */
- PyObject *found = PyDict_GetItemWithError (d, file.get ());
+ PyObject *found = PyDict_GetItemWithError (globals, file.get ());
So on the whole I'd prefer to drop this from this series. That way it
won't conflict or cause problems with the stuff I'm planning to send.
Also you've made this into a template class, which seems alright -- but
not used in the series. Maybe there's some later series depending on
this? That kind of thing is good to mention if so.
Tom
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 1/4] gdb/python: add gdbpy_borrowed_ref
2026-05-15 16:56 ` Tom Tromey
@ 2026-05-18 14:25 ` Matthieu Longo
2026-05-19 19:25 ` Tom Tromey
0 siblings, 1 reply; 23+ messages in thread
From: Matthieu Longo @ 2026-05-18 14:25 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
On 15/05/2026 17:56, Tom Tromey wrote:
>>>>>> Matthieu Longo <matthieu.longo@arm.com> writes:
>
>> From: Tom Tromey <tom@tromey.com>
>> This adds a new gdbpy_borrowed_ref class. This class is primarily for
>> code "documentation" purposes -- it makes it clear to the reader that
>> a given reference is borrowed. However, it also adds a tiny bit of
>> safety, in that conversion to gdbpy_ref<> will acquire a new
>> reference.
>
>> +template <class T = PyObject>
>> +class gdbpy_borrowed_ref
>> +{
>
> On my "safety" branch I ended up rewriting this to have a base class
> that allows NULL and a derived class that does not.
>
> And my initial review -- the one that didn't get sent -- suggested using
> this.
>
> But I tend to think this patch isn't needed yet. It's nice for the
> safety series but in this series it is only used a little in patch 2,
> and not even every spot that could/should use it -- like I noticed:
>
> /* PyDict_GetItemWithError returns a borrowed reference. */
> - PyObject *found = PyDict_GetItemWithError (d, file.get ());
> + PyObject *found = PyDict_GetItemWithError (globals, file.get ());
>
I can replace the raw PyObject * by gdbpy_borrowed_ref<>.
> So on the whole I'd prefer to drop this from this series. That way it
> won't conflict or cause problems with the stuff I'm planning to send.
>
A lot of upcoming patches are using gdbpy_borrowed_ref<> because it simplified a lot the code in
some places.
Dropping it is going to make things definitely more difficult on my side.
> Also you've made this into a template class, which seems alright -- but
> not used in the series. Maybe there's some later series depending on
> this? That kind of thing is good to mention if so.
Yes, it is used later.
>
> Tom
Given those information, do you still want to drop it ?
Matthieu
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 2/4] gdb/python: eval_python_command returns result of the evaluation
2026-05-15 16:51 ` Tom Tromey
@ 2026-05-18 14:37 ` Matthieu Longo
2026-05-19 19:24 ` Tom Tromey
0 siblings, 1 reply; 23+ messages in thread
From: Matthieu Longo @ 2026-05-18 14:37 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
On 15/05/2026 17:51, Tom Tromey wrote:
>>>>>> Matthieu Longo <matthieu.longo@arm.com> writes:
>
>> +++ b/gdbsupport/gdb_ref_ptr.h
>> @@ -179,6 +179,11 @@ class ref_ptr
>> return m_obj;
>> }
>
>> + operator bool () const noexcept
>> + {
>> + return m_obj != nullptr;
>> + }
>
> gdb is normally explicit instead, and since there aren't many call sites
> I think this operator should be dropped and those spots updated to check
> against nullptr.
>
> Tom
Here is the updated diff.
(1)
if (eval_python_command (...) == nullptr)
VS (2)
if (! eval_python_command (...))
As you can see below, (1) is significantly more verbose than (2) and makes more difficult to respect
the "80 characters per line" rule.
Matthieu
diff --git a/gdb/python/py-gdb-readline.c b/gdb/python/py-gdb-readline.c
index e8e2c23547c..f3d87e0ae25 100644
--- a/gdb/python/py-gdb-readline.c
+++ b/gdb/python/py-gdb-readline.c
@@ -114,7 +114,7 @@ class GdbRemoveReadlineFinder(MetaPathFinder):\n\
\n\
sys.meta_path.insert(2, GdbRemoveReadlineFinder())\n\
";
- if (eval_python_command (code, Py_file_input) == 0)
+ if (eval_python_command (code, Py_file_input) != nullptr)
PyOS_ReadlineFunctionPointer = gdbpy_readline_wrapper;
return 0;
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 82f3262ae07..a26e33c33a1 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -1342,8 +1342,8 @@ class gdbpy_memoizing_registry_storage
gdb::unordered_map<val_type *, obj_type *> m_objects;
};
-extern int eval_python_command (const char *command, int start_symbol,
- const char *filename = nullptr);
+extern gdbpy_ref<> eval_python_command
+ (const char *command, int start_symbol, const char *filename = nullptr);
/* The following four functions are refcount-safe wrappers around
Py_RETURN_{NONE,TRUE,FALSE,NOTIMPLEMENTED}. */
diff --git a/gdb/python/python.c b/gdb/python/python.c
index fd254a340d8..2b266a56759 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -298,44 +298,43 @@ gdbpy_check_quit_flag (const struct extension_language_defn *extlang)
Python start symbol, and does not automatically print the stack on
errors. FILENAME is used to set the file name in error messages;
NULL means that this is evaluating a string, not the contents of a
- file. */
+ file.
+ Return the result of the evaluation on success, NULL on failure. */
-int
+gdbpy_ref<>
eval_python_command (const char *command, int start_symbol,
const char *filename)
{
- PyObject *m, *d;
-
- m = PyImport_AddModule ("__main__");
- if (m == NULL)
- return -1;
+ gdbpy_borrowed_ref<> mainmod = PyImport_AddModule ("__main__");
+ if (mainmod == nullptr)
+ return nullptr;
- d = PyModule_GetDict (m);
- if (d == NULL)
- return -1;
+ gdbpy_borrowed_ref<> globals = PyModule_GetDict (mainmod);
+ if (globals == nullptr)
+ return nullptr;
bool file_set = false;
if (filename != nullptr)
{
gdbpy_ref<> file = host_string_to_python_string ("__file__");
if (file == nullptr)
- return -1;
+ return nullptr;
/* PyDict_GetItemWithError returns a borrowed reference. */
- PyObject *found = PyDict_GetItemWithError (d, file.get ());
+ PyObject *found = PyDict_GetItemWithError (globals, file.get ());
if (found == nullptr)
{
if (PyErr_Occurred ())
- return -1;
+ return nullptr;
gdbpy_ref<> filename_obj = host_string_to_python_string (filename);
if (filename_obj == nullptr)
- return -1;
+ return nullptr;
- if (PyDict_SetItem (d, file.get (), filename_obj.get ()) < 0)
- return -1;
- if (PyDict_SetItemString (d, "__cached__", Py_None) < 0)
- return -1;
+ if (PyDict_SetItem (globals, file.get (), filename_obj.get ()) < 0)
+ return nullptr;
+ if (PyDict_SetItemString (globals, "__cached__", Py_None) < 0)
+ return nullptr;
file_set = true;
}
@@ -348,12 +347,12 @@ eval_python_command (const char *command, int start_symbol,
: filename,
start_symbol));
- int result = -1;
+ gdbpy_ref<> eval_result;
if (code != nullptr)
{
- gdbpy_ref<> eval_result (PyEval_EvalCode (code.get (), d, d));
- if (eval_result != nullptr)
- result = 0;
+ eval_result.reset (PyEval_EvalCode (code.get (), globals, globals));
+ if (eval_result == nullptr)
+ gdb_assert (PyErr_Occurred ());
}
if (file_set)
@@ -361,21 +360,21 @@ eval_python_command (const char *command, int start_symbol,
/* If there's already an exception occurring, preserve it and
restore it before returning from this function. */
std::optional<gdbpy_err_fetch> save_error;
- if (result < 0)
+ if (eval_result == nullptr)
save_error.emplace ();
/* CPython also just ignores errors here. These should be
expected to be exceedingly rare anyway. */
- if (PyDict_DelItemString (d, "__file__") < 0)
+ if (PyDict_DelItemString (globals, "__file__") < 0)
PyErr_Clear ();
- if (PyDict_DelItemString (d, "__cached__") < 0)
+ if (PyDict_DelItemString (globals, "__cached__") < 0)
PyErr_Clear ();
if (save_error.has_value ())
save_error->restore ();
}
- return result;
+ return eval_result;
}
/* Implementation of the gdb "python-interactive" command. */
@@ -384,7 +383,7 @@ static void
python_interactive_command (const char *arg, int from_tty)
{
struct ui *ui = current_ui;
- int err;
+ bool err;
scoped_restore save_async = make_scoped_restore (¤t_ui->async, 0);
@@ -396,11 +395,11 @@ python_interactive_command (const char *arg, int from_tty)
{
std::string script = std::string (arg) + "\n";
/* Py_single_input causes the result to be displayed. */
- err = eval_python_command (script.c_str (), Py_single_input);
+ err = (eval_python_command (script.c_str (), Py_single_input) == nullptr);
}
else
{
- err = PyRun_InteractiveLoop (ui->instream, "<stdin>");
+ err = PyRun_InteractiveLoop (ui->instream, "<stdin>") != 0;
dont_repeat ();
}
@@ -411,6 +410,7 @@ python_interactive_command (const char *arg, int from_tty)
/* Like PyRun_SimpleFile, but if there is an exception, it is not
automatically displayed. FILE is the Python script to run named
FILENAME.
+ Return true on success, false on failure.
On Windows hosts few users would build Python themselves (this is no
trivial task on this platform), and thus use binaries built by
@@ -421,11 +421,11 @@ python_interactive_command (const char *arg, int from_tty)
A FILE * from one runtime does not necessarily operate correctly in
the other runtime. */
-static int
+static bool
python_run_simple_file (FILE *file, const char *filename)
{
std::string contents = read_remainder_of_file (file);
- return eval_python_command (contents.c_str (), Py_file_input, filename);
+ return eval_python_command (contents.c_str (), Py_file_input, filename) != nullptr;
}
/* Given a command_line, return a command string suitable for passing
@@ -459,8 +459,7 @@ gdbpy_eval_from_control_command (const struct extension_language_defn *extlang,
gdbpy_enter enter_py;
std::string script = compute_python_string (cmd->body_list_0.get ());
- int ret = eval_python_command (script.c_str (), Py_file_input);
- if (ret != 0)
+ if (eval_python_command (script.c_str (), Py_file_input) == nullptr)
gdbpy_handle_exception ();
}
@@ -476,8 +475,7 @@ python_command (const char *arg, int from_tty)
arg = skip_spaces (arg);
if (arg && *arg)
{
- int ret = eval_python_command (arg, Py_file_input);
- if (ret != 0)
+ if (eval_python_command (arg, Py_file_input) == nullptr)
gdbpy_handle_exception ();
}
else
@@ -1120,8 +1118,7 @@ gdbpy_source_script (const struct extension_language_defn *extlang,
FILE *file, const char *filename)
{
gdbpy_enter enter_py;
- int result = python_run_simple_file (file, filename);
- if (result != 0)
+ if (! python_run_simple_file (file, filename))
gdbpy_handle_exception ();
}
@@ -1827,8 +1824,7 @@ gdbpy_source_objfile_script (const struct extension_language_defn *extlang,
scoped_restore restire_current_objfile
= make_scoped_restore (&gdbpy_current_objfile, objfile);
- int result = python_run_simple_file (file, filename);
- if (result != 0)
+ if (! python_run_simple_file (file, filename))
gdbpy_print_stack ();
}
@@ -1850,8 +1846,7 @@ gdbpy_execute_objfile_script (const struct extension_language_defn *extlang,
scoped_restore restire_current_objfile
= make_scoped_restore (&gdbpy_current_objfile, objfile);
- int ret = eval_python_command (script, Py_file_input);
- if (ret != 0)
+ if (eval_python_command (script, Py_file_input) == nullptr)
gdbpy_print_stack ();
}
diff --git a/gdb/varobj.c b/gdb/varobj.c
index edd94ea4963..a8c991e50eb 100644
--- a/gdb/varobj.c
+++ b/gdb/varobj.c
@@ -1365,20 +1365,13 @@ void
varobj_set_visualizer (struct varobj *var, const char *visualizer)
{
#if HAVE_PYTHON
- PyObject *mainmod;
-
if (!gdb_python_initialized)
return;
gdbpy_enter_varobj enter_py (var);
- mainmod = PyImport_AddModule ("__main__");
- gdbpy_ref<> globals
- = gdbpy_ref<>::new_reference (PyModule_GetDict (mainmod));
- gdbpy_ref<> constructor (PyRun_String (visualizer, Py_eval_input,
- globals.get (), globals.get ()));
-
- if (constructor == NULL)
+ auto constructor = eval_python_command (visualizer, Py_eval_input);
+ if (constructor == nullptr)
{
gdbpy_print_stack ();
error (_("Could not evaluate visualizer expression: %s"), visualizer);
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 2/4] gdb/python: eval_python_command returns result of the evaluation
2026-05-18 14:37 ` Matthieu Longo
@ 2026-05-19 19:24 ` Tom Tromey
2026-05-20 10:07 ` Matthieu Longo
0 siblings, 1 reply; 23+ messages in thread
From: Tom Tromey @ 2026-05-19 19:24 UTC (permalink / raw)
To: Matthieu Longo; +Cc: Tom Tromey, gdb-patches
>>>>> Matthieu Longo <matthieu.longo@arm.com> writes:
> (1)
> if (eval_python_command (...) == nullptr)
> VS (2)
> if (! eval_python_command (...))
> As you can see below, (1) is significantly more verbose than (2) and
> makes more difficult to respect the "80 characters per line" rule.
I'm probably not the best person to advocate for this, since in olden
times I routinely wrote checks in the concise style, but anyway I think
the rationale is that of the competing interests at play, being clear &
explicit wins out over formatting.
> - if (eval_python_command (code, Py_file_input) == 0)
> + if (eval_python_command (code, Py_file_input) != nullptr)
Should this be == nullptr instead?
thanks,
Tom
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 1/4] gdb/python: add gdbpy_borrowed_ref
2026-05-18 14:25 ` Matthieu Longo
@ 2026-05-19 19:25 ` Tom Tromey
2026-05-20 16:04 ` Matthieu Longo
0 siblings, 1 reply; 23+ messages in thread
From: Tom Tromey @ 2026-05-19 19:25 UTC (permalink / raw)
To: Matthieu Longo; +Cc: Tom Tromey, gdb-patches
>>>>> "Matthieu" == Matthieu Longo <matthieu.longo@arm.com> writes:
>> So on the whole I'd prefer to drop this from this series. That way it
>> won't conflict or cause problems with the stuff I'm planning to send.
Matthieu> A lot of upcoming patches are using gdbpy_borrowed_ref<> because it
Matthieu> simplified a lot the code in some places.
Matthieu> Dropping it is going to make things definitely more difficult on my side.
Ok, no problem.
I'll update the safety series to use templates.
Tom
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 2/4] gdb/python: eval_python_command returns result of the evaluation
2026-05-19 19:24 ` Tom Tromey
@ 2026-05-20 10:07 ` Matthieu Longo
2026-05-20 15:06 ` Tom Tromey
0 siblings, 1 reply; 23+ messages in thread
From: Matthieu Longo @ 2026-05-20 10:07 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
On 19/05/2026 20:24, Tom Tromey wrote:
>>>>>> Matthieu Longo <matthieu.longo@arm.com> writes:
>
>> (1)
>> if (eval_python_command (...) == nullptr)
>> VS (2)
>> if (! eval_python_command (...))
>
>> As you can see below, (1) is significantly more verbose than (2) and
>> makes more difficult to respect the "80 characters per line" rule.
>
> I'm probably not the best person to advocate for this, since in olden
> times I routinely wrote checks in the concise style, but anyway I think
> the rationale is that of the competing interests at play, being clear &
> explicit wins out over formatting.
>
>> - if (eval_python_command (code, Py_file_input) == 0)
>> + if (eval_python_command (code, Py_file_input) != nullptr)
>
> Should this be == nullptr instead?
No, eval_python_command() returns nullptr on failure, otherwise the result of the evaluation.
Previously, 0 indicated no errors.
If the evaluation works, PyOS_ReadlineFunctionPointer is assigned.
So the change looks correct to me.
>
> thanks,
> Tom
Regards,
Matthieu
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 2/4] gdb/python: eval_python_command returns result of the evaluation
2026-05-20 10:07 ` Matthieu Longo
@ 2026-05-20 15:06 ` Tom Tromey
0 siblings, 0 replies; 23+ messages in thread
From: Tom Tromey @ 2026-05-20 15:06 UTC (permalink / raw)
To: Matthieu Longo; +Cc: Tom Tromey, gdb-patches
>> Should this be == nullptr instead?
Matthieu> No, eval_python_command() returns nullptr on failure, otherwise the result of the evaluation.
Matthieu> Previously, 0 indicated no errors.
Matthieu> If the evaluation works, PyOS_ReadlineFunctionPointer is assigned.
Matthieu> So the change looks correct to me.
Thanks. This patch is ok then.
Approved-By: Tom Tromey <tom@tromey.com>
Tom
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 1/4] gdb/python: add gdbpy_borrowed_ref
2026-05-19 19:25 ` Tom Tromey
@ 2026-05-20 16:04 ` Matthieu Longo
2026-05-21 14:35 ` Tom Tromey
0 siblings, 1 reply; 23+ messages in thread
From: Matthieu Longo @ 2026-05-20 16:04 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
On 19/05/2026 20:25, Tom Tromey wrote:
>>>>>> "Matthieu" == Matthieu Longo <matthieu.longo@arm.com> writes:
>
>>> So on the whole I'd prefer to drop this from this series. That way it
>>> won't conflict or cause problems with the stuff I'm planning to send.
>
> Matthieu> A lot of upcoming patches are using gdbpy_borrowed_ref<> because it
> Matthieu> simplified a lot the code in some places.
> Matthieu> Dropping it is going to make things definitely more difficult on my side.
>
> Ok, no problem.
>
> I'll update the safety series to use templates.
>
> Tom
Just to be sure, should I take your last sentence as an approval of the patch ?
Matthieu
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 1/4] gdb/python: add gdbpy_borrowed_ref
2026-05-20 16:04 ` Matthieu Longo
@ 2026-05-21 14:35 ` Tom Tromey
2026-05-21 23:18 ` Tom Tromey
0 siblings, 1 reply; 23+ messages in thread
From: Tom Tromey @ 2026-05-21 14:35 UTC (permalink / raw)
To: Matthieu Longo; +Cc: Tom Tromey, gdb-patches
Matthieu> Just to be sure, should I take your last sentence as an approval of the patch ?
If you're going to move forward I think either you should wait for my
series, which may be slow; or use the latest patch as the base, because
IMO the opt/non-top split is important.
thanks,
Tom
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 1/4] gdb/python: add gdbpy_borrowed_ref
2026-05-21 14:35 ` Tom Tromey
@ 2026-05-21 23:18 ` Tom Tromey
0 siblings, 0 replies; 23+ messages in thread
From: Tom Tromey @ 2026-05-21 23:18 UTC (permalink / raw)
To: Tom Tromey; +Cc: Matthieu Longo, gdb-patches
Matthieu> Just to be sure, should I take your last sentence as an approval of the patch ?
Tom> If you're going to move forward I think either you should wait for my
Tom> series, which may be slow; or use the latest patch as the base, because
Tom> IMO the opt/non-top split is important.
I found some time to address this today & I'll send an updated series shortly.
I'd be happy to land patch 1 sooner if that's convenient.
Tom
^ permalink raw reply [flat|nested] 23+ messages in thread
end of thread, other threads:[~2026-05-21 23:19 UTC | newest]
Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-04-28 16:24 [PATCH v2 0/4] gdb/python: more fixes again for Python limited C API support Matthieu Longo
2026-04-28 16:24 ` [PATCH v2 1/4] gdb/python: add gdbpy_borrowed_ref Matthieu Longo
2026-05-15 16:56 ` Tom Tromey
2026-05-18 14:25 ` Matthieu Longo
2026-05-19 19:25 ` Tom Tromey
2026-05-20 16:04 ` Matthieu Longo
2026-05-21 14:35 ` Tom Tromey
2026-05-21 23:18 ` Tom Tromey
2026-04-28 16:24 ` [PATCH v2 2/4] gdb/python: eval_python_command returns result of the evaluation Matthieu Longo
2026-05-15 16:51 ` Tom Tromey
2026-05-18 14:37 ` Matthieu Longo
2026-05-19 19:24 ` Tom Tromey
2026-05-20 10:07 ` Matthieu Longo
2026-05-20 15:06 ` Tom Tromey
2026-04-28 16:24 ` [PATCH v2 3/4] gdb/python: migrate Python initialization to use the new config API (PEP 741) Matthieu Longo
2026-05-14 19:25 ` Tom Tromey
2026-04-28 16:24 ` [PATCH v2 4/4] gdb/python: work around missing symbols not yet part of Python limited API Matthieu Longo
2026-05-14 19:26 ` Tom Tromey
2026-05-15 10:10 ` Matthieu Longo
2026-05-14 10:23 ` [PATCH v2 0/4] gdb/python: more fixes again for Python limited C API support Matthieu Longo
2026-05-14 19:27 ` Tom Tromey
2026-05-15 9:26 ` Matthieu Longo
2026-05-15 14:48 ` Tom Tromey
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox