* [PATCH v1 1/4] gdb/python: add gdbpy_borrowed_ref
2026-04-09 10:51 [PATCH v1 0/4] gdb/python: more fixes again for Python limited C API support Matthieu Longo
@ 2026-04-09 10:51 ` Matthieu Longo
2026-04-09 10:51 ` [PATCH v1 2/4] gdb/python: eval_python_command returns both exit code and result Matthieu Longo
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Matthieu Longo @ 2026-04-09 10:51 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 | 114 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 114 insertions(+)
diff --git a/gdb/python/py-ref.h b/gdb/python/py-ref.h
index dc0b14814af..80a08b55288 100644
--- a/gdb/python/py-ref.h
+++ b/gdb/python/py-ref.h
@@ -41,6 +41,120 @@ 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;
+ }
+
+ /* Allow a (checked) conversion to any subclass of PyObject. */
+ template<typename TObj,
+ typename = std::is_convertible<TObj *, T *>>
+ operator TObj * ()
+ {
+ //gdb_assert (PyObject_TypeCheck (m_obj, T::corresponding_object_type));
+ return static_cast<TObj *> (m_obj);
+ }
+
+ operator T * () const noexcept
+ {
+ return m_obj;
+ }
+
+ /* When converting a borrowed reference to a gdbpy_ref<>, a new
+ reference is acquired. */
+ template <typename TObj,
+ typename = std::is_convertible<TObj *, T *>>
+ operator gdbpy_ref<TObj> ()
+ {
+ gdb_assert (m_obj != nullptr);
+ return gdbpy_ref<TObj>::new_reference (m_obj);
+ }
+
+ gdbpy_ref<T> strong_ref () const noexcept
+ {
+ return static_cast<gdbpy_ref<T>> (*this);
+ }
+
+ /* Let users refer to members of the underlying pointer. */
+ T *operator-> () const noexcept
+ {
+ return m_obj;
+ }
+
+ T *get () 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.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH v1 2/4] gdb/python: eval_python_command returns both exit code and result
2026-04-09 10:51 [PATCH v1 0/4] gdb/python: more fixes again for Python limited C API support Matthieu Longo
2026-04-09 10:51 ` [PATCH v1 1/4] gdb/python: add gdbpy_borrowed_ref Matthieu Longo
@ 2026-04-09 10:51 ` Matthieu Longo
2026-04-09 10:51 ` [PATCH v1 3/4] gdb/python: migrate Python initialization to use the new config API (PEP 741) Matthieu Longo
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Matthieu Longo @ 2026-04-09 10:51 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 both the exit code and the result
of the evaluation.
This patch updates eval_python_command() to return both values and
adjusts existing callers accordingly. It also 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 | 3 +-
gdb/python/python-internal.h | 4 +--
gdb/python/python.c | 54 +++++++++++++++++++-----------------
gdb/varobj.c | 12 ++------
4 files changed, 35 insertions(+), 38 deletions(-)
diff --git a/gdb/python/py-gdb-readline.c b/gdb/python/py-gdb-readline.c
index e8e2c23547c..eedeb659c18 100644
--- a/gdb/python/py-gdb-readline.c
+++ b/gdb/python/py-gdb-readline.c
@@ -114,7 +114,8 @@ class GdbRemoveReadlineFinder(MetaPathFinder):\n\
\n\
sys.meta_path.insert(2, GdbRemoveReadlineFinder())\n\
";
- if (eval_python_command (code, Py_file_input) == 0)
+ auto [ret, _] = eval_python_command (code, Py_file_input);
+ if (ret == 0)
PyOS_ReadlineFunctionPointer = gdbpy_readline_wrapper;
return 0;
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 37bc37691fe..ca70ca57fd2 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 std::pair<int, 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 4182c699cb5..85bd88cd243 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -299,42 +299,40 @@ gdbpy_check_quit_flag (const struct extension_language_defn *extlang)
NULL means that this is evaluating a string, not the contents of a
file. */
-int
+std::pair<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 std::make_pair(-1, nullptr);
- d = PyModule_GetDict (m);
- if (d == NULL)
- return -1;
+ gdbpy_borrowed_ref<> globals = PyModule_GetDict (mainmod);
+ if (globals == nullptr)
+ return std::make_pair(-1, nullptr);
bool file_set = false;
if (filename != nullptr)
{
gdbpy_ref<> file = host_string_to_python_string ("__file__");
if (file == nullptr)
- return -1;
+ return std::make_pair(-1, 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 std::make_pair(-1, nullptr);
gdbpy_ref<> filename_obj = host_string_to_python_string (filename);
if (filename_obj == nullptr)
- return -1;
+ return std::make_pair(-1, 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 std::make_pair(-1, nullptr);
+ if (PyDict_SetItemString (globals, "__cached__", Py_None) < 0)
+ return std::make_pair(-1, nullptr);
file_set = true;
}
@@ -348,9 +346,10 @@ eval_python_command (const char *command, int start_symbol,
start_symbol));
int result = -1;
+ gdbpy_ref<> eval_result;
if (code != nullptr)
{
- gdbpy_ref<> eval_result (PyEval_EvalCode (code.get (), d, d));
+ eval_result.reset (PyEval_EvalCode (code.get (), globals, globals));
if (eval_result != nullptr)
result = 0;
}
@@ -365,16 +364,16 @@ eval_python_command (const char *command, int start_symbol,
/* 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 std::make_pair(result, eval_result);
}
/* Implementation of the gdb "python-interactive" command. */
@@ -395,7 +394,8 @@ 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);
+ std::tie (err, std::ignore)
+ = eval_python_command (script.c_str (), Py_single_input);
}
else
{
@@ -424,7 +424,9 @@ static int
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);
+ auto [ret, _]
+ = eval_python_command (contents.c_str (), Py_file_input, filename);
+ return ret;
}
/* Given a command_line, return a command string suitable for passing
@@ -457,7 +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);
+ auto [ret, _] = eval_python_command (script.c_str (), Py_file_input);
if (ret != 0)
gdbpy_handle_exception ();
}
@@ -474,7 +476,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);
+ auto [ret, _] = eval_python_command (arg, Py_file_input);
if (ret != 0)
gdbpy_handle_exception ();
}
@@ -1848,7 +1850,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);
+ auto [ret, _] = eval_python_command (script, Py_file_input);
if (ret != 0)
gdbpy_print_stack ();
}
diff --git a/gdb/varobj.c b/gdb/varobj.c
index edd94ea4963..b20fc78eb28 100644
--- a/gdb/varobj.c
+++ b/gdb/varobj.c
@@ -1365,20 +1365,14 @@ 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 [ret_code, constructor]
+ = eval_python_command (visualizer, Py_eval_input);
+ if (ret_code != 0 || constructor == nullptr)
{
gdbpy_print_stack ();
error (_("Could not evaluate visualizer expression: %s"), visualizer);
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH v1 3/4] gdb/python: migrate Python initialization to use the new config API (PEP 741)
2026-04-09 10:51 [PATCH v1 0/4] gdb/python: more fixes again for Python limited C API support Matthieu Longo
2026-04-09 10:51 ` [PATCH v1 1/4] gdb/python: add gdbpy_borrowed_ref Matthieu Longo
2026-04-09 10:51 ` [PATCH v1 2/4] gdb/python: eval_python_command returns both exit code and result Matthieu Longo
@ 2026-04-09 10:51 ` Matthieu Longo
2026-04-09 10:51 ` [PATCH v1 4/4] gdb/python: work around missing symbols not yet part of Python limited API Matthieu Longo
2026-04-15 9:18 ` [PATCH v1 0/4] gdb/python: more fixes again for Python limited C API support Matthieu Longo
4 siblings, 0 replies; 6+ messages in thread
From: Matthieu Longo @ 2026-04-09 10:51 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 | 128 +++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 127 insertions(+), 1 deletion(-)
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 85bd88cd243..1a0ff29d1d4 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -2216,6 +2216,100 @@ 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;
+ bool m_py_initialized;
+};
+
+gdb_PyInitializer::gdb_PyInitializer ()
+ : m_config (PyInitConfig_Create ()),
+ m_err (false),
+ m_py_initialized (false)
+{
+ if (m_config == nullptr)
+ {
+ throw_error (GENERIC_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)
+ throw_error (GENERIC_ERROR,
+ _("Python initialization failed: %s"),
+ err_msg);
+
+ int exit_code;
+ gdb_assert (PyInitConfig_GetExitCode (m_config, &exit_code) != 0);
+ throw_error (GENERIC_ERROR,
+ _("Python initialization failed with exit status: %d"),
+ exit_code);
+}
+
+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
@@ -2549,7 +2643,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);
@@ -2585,6 +2680,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)
+ {
+ gdb_printf ("%s\n", exc.what ());
+ py_isinitialized = false;
+ }
+ return py_isinitialized;
#endif
}
--
2.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH v1 4/4] gdb/python: work around missing symbols not yet part of Python limited API
2026-04-09 10:51 [PATCH v1 0/4] gdb/python: more fixes again for Python limited C API support Matthieu Longo
` (2 preceding siblings ...)
2026-04-09 10:51 ` [PATCH v1 3/4] gdb/python: migrate Python initialization to use the new config API (PEP 741) Matthieu Longo
@ 2026-04-09 10:51 ` Matthieu Longo
2026-04-15 9:18 ` [PATCH v1 0/4] gdb/python: more fixes again for Python limited C API support Matthieu Longo
4 siblings, 0 replies; 6+ messages in thread
From: Matthieu Longo @ 2026-04-09 10:51 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 ca70ca57fd2..e85bc51f2fc 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.53.0
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [PATCH v1 0/4] gdb/python: more fixes again for Python limited C API support
2026-04-09 10:51 [PATCH v1 0/4] gdb/python: more fixes again for Python limited C API support Matthieu Longo
` (3 preceding siblings ...)
2026-04-09 10:51 ` [PATCH v1 4/4] gdb/python: work around missing symbols not yet part of Python limited API Matthieu Longo
@ 2026-04-15 9:18 ` Matthieu Longo
4 siblings, 0 replies; 6+ messages in thread
From: Matthieu Longo @ 2026-04-15 9:18 UTC (permalink / raw)
To: gdb-patches, Tom Tromey
On 09/04/2026 11:51, 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.
>
> Regards,
> Matthieu
>
> [1]: https://inbox.sourceware.org/gdb-patches/20260222200759.1587070-2-tom@tromey.com/
>
>
> Matthieu Longo (3):
> gdb/python: eval_python_command returns both exit code and result
> 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 | 3 +-
> gdb/python/py-ref.h | 114 +++++++++++++++
> gdb/python/python-internal.h | 7 +-
> gdb/python/python-limited-api-missing.h | 62 ++++++++
> gdb/python/python.c | 182 ++++++++++++++++++++----
> gdb/varobj.c | 12 +-
> 6 files changed, 341 insertions(+), 39 deletions(-)
> create mode 100644 gdb/python/python-limited-api-missing.h
>
Hi Tom,
Do you know when you will have a chance to look at this patch series ?
If you are short in time, you might want to focus only on patch 1 for now. As you probably suspect,
this patch is going to be used in various places in the upcoming patches. I would like to get
gdbpy_borrowed_ref merged, or at least agreed upon even if it requires some reworks.
Regards,
Matthieu
^ permalink raw reply [flat|nested] 6+ messages in thread