* [PATCH v3 1/7] gdb: introduce rgb_color type to simplify existing code
2026-03-09 17:56 [PATCH v3 0/7] gdb: more fixes for Python limited C API support Matthieu Longo
@ 2026-03-09 17:56 ` Matthieu Longo
2026-03-09 19:22 ` Tom Tromey
2026-03-09 17:56 ` [PATCH v3 2/7] gdb: fail configure if Python version is too old for limited API Matthieu Longo
` (6 subsequent siblings)
7 siblings, 1 reply; 25+ messages in thread
From: Matthieu Longo @ 2026-03-09 17:56 UTC (permalink / raw)
To: gdb-patches, Tom Tromey; +Cc: Matthieu Longo
This patch replaces the raw uint8[3] buffer used to represent RGB values
with a more convenient wrapper, rgb_color, around std::array<uint8_t, 3>.
It also changes the return type of ui_file_style::color::get_rgb to
rgb_color instead of filling a caller-provided buffer, and updates all
callers accordingly.
This expected benefit of this change consists in:
- removing the manual size handling.
- proving accessors without using hard-coded indexes.
- making the API safer.
- simplifying call sites.
This refactoring does not introduce any functional change.
---
gdb/guile/scm-color.c | 9 ++++----
gdb/mingw-hdep.c | 5 ++---
gdb/python/py-color.c | 10 ++++-----
gdb/tui/tui-io.c | 3 +--
gdb/ui-style.c | 37 +++++++++++++--------------------
gdb/ui-style.h | 37 ++++++++++++++++++++++++++++++++-
gdb/unittests/style-selftests.c | 10 ++++-----
7 files changed, 67 insertions(+), 44 deletions(-)
diff --git a/gdb/guile/scm-color.c b/gdb/guile/scm-color.c
index c3a1d663215..4ffe303578c 100644
--- a/gdb/guile/scm-color.c
+++ b/gdb/guile/scm-color.c
@@ -339,11 +339,10 @@ gdbscm_color_components (SCM self)
if (!color.is_direct ())
gdbscm_misc_error (FUNC_NAME, SCM_ARG1, self, "color is not direct");
- uint8_t rgb[3] = {};
- color.get_rgb (rgb);
- SCM red = scm_from_uint8 (rgb[0]);
- SCM green = scm_from_uint8 (rgb[1]);
- SCM blue = scm_from_uint8 (rgb[2]);
+ rgb_color rgb = color.get_rgb ();
+ SCM red = scm_from_uint8 (rgb.r ());
+ SCM green = scm_from_uint8 (rgb.g ());
+ SCM blue = scm_from_uint8 (rgb.b ());
return scm_list_3 (red, green, blue);
}
diff --git a/gdb/mingw-hdep.c b/gdb/mingw-hdep.c
index efad50c4cbe..90ebe252f57 100644
--- a/gdb/mingw-hdep.c
+++ b/gdb/mingw-hdep.c
@@ -192,11 +192,10 @@ gdb_select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
static int
rgb_to_16colors (const ui_file_style::color &color)
{
- uint8_t rgb[3];
- color.get_rgb (rgb);
+ rgb_color rgb = color.get_rgb ();
int retval = 0;
- for (int i = 0; i < 3; i++)
+ for (int i = 0; i < rgb.size (); i++)
{
/* Subdivide 256 possible values of each RGB component into 3
regions: no color, normal color, bright color. 256 / 3 = 85,
diff --git a/gdb/python/py-color.c b/gdb/python/py-color.c
index 04f7addec2f..971209958cf 100644
--- a/gdb/python/py-color.c
+++ b/gdb/python/py-color.c
@@ -108,11 +108,9 @@ get_attr (PyObject *obj, PyObject *attr_name)
if (color.is_direct ()
&& !PyUnicode_CompareWithASCIIString (attr_name, "components"))
{
- uint8_t rgb[3];
- color.get_rgb (rgb);
-
- gdbpy_ref<> rgb_objects[3];
- for (int i = 0; i < 3; ++i)
+ rgb_color rgb = color.get_rgb ();
+ std::array<gdbpy_ref<>, rgb.size ()> rgb_objects;
+ for (auto i = 0u; i < rgb_objects.size (); ++i)
{
rgb_objects[i] = gdb_py_object_from_ulongest (rgb[i]);
if (rgb_objects[i] == nullptr)
@@ -123,7 +121,7 @@ get_attr (PyObject *obj, PyObject *attr_name)
if (comp == nullptr)
return nullptr;
- for (int i = 0; i < 3; ++i)
+ for (auto i = 0u; i < rgb_objects.size (); ++i)
if (PyTuple_SetItem (comp.get (), i, rgb_objects[i].release ()) < 0)
return nullptr;
diff --git a/gdb/tui/tui-io.c b/gdb/tui/tui-io.c
index f673fbf36f6..a9a50446e8a 100644
--- a/gdb/tui/tui-io.c
+++ b/gdb/tui/tui-io.c
@@ -255,8 +255,7 @@ get_color (const ui_file_style::color &color, int *result)
int next = color_map.size () + 8;
if (next >= COLORS)
return false;
- uint8_t rgb[3];
- color.get_rgb (rgb);
+ rgb_color rgb = color.get_rgb ();
/* We store RGB as 0..255, but curses wants 0..1000. */
if (init_color (next, rgb[0] * 1000 / 255, rgb[1] * 1000 / 255,
rgb[2] * 1000 / 255) == ERR)
diff --git a/gdb/ui-style.c b/gdb/ui-style.c
index 7ab466e2407..05725e314e4 100644
--- a/gdb/ui-style.c
+++ b/gdb/ui-style.c
@@ -53,7 +53,7 @@ static compiled_regex ansi_regex (ansi_regex_text, REG_EXTENDED,
/* This maps 8-color palette to RGB triples. The values come from
plain linux terminal. */
-static const uint8_t palette_8colors[][3] = {
+static const rgb_color palette_8colors[] = {
{ 1, 1, 1 }, /* Black. */
{ 222, 56, 43 }, /* Red. */
{ 57, 181, 74 }, /* Green. */
@@ -66,7 +66,7 @@ static const uint8_t palette_8colors[][3] = {
/* This maps 16-color palette to RGB triples. The values come from xterm. */
-static const uint8_t palette_16colors[][3] = {
+static const rgb_color palette_16colors[] = {
{ 0, 0, 0 }, /* Black. */
{ 205, 0, 0 }, /* Red. */
{ 0, 205, 0 }, /* Green. */
@@ -166,25 +166,22 @@ ui_file_style::color::to_string () const
/* See ui-style.h. */
-void
-ui_file_style::color::get_rgb (uint8_t *rgb) const
+rgb_color
+ui_file_style::color::get_rgb () const
{
+ rgb_color rgb;
if (m_color_space == color_space::RGB_24BIT)
- {
- rgb[0] = m_red;
- rgb[1] = m_green;
- rgb[2] = m_blue;
- }
+ rgb = rgb_color (m_red, m_green, m_blue);
else if (m_color_space == color_space::ANSI_8COLOR
&& 0 <= m_value && m_value <= 7)
- memcpy (rgb, palette_8colors[m_value], 3 * sizeof (uint8_t));
+ rgb = palette_8colors[m_value];
else if (m_color_space == color_space::AIXTERM_16COLOR
&& 0 <= m_value && m_value <= 15)
- memcpy (rgb, palette_16colors[m_value], 3 * sizeof (uint8_t));
+ rgb = palette_16colors[m_value];
else if (m_color_space != color_space::XTERM_256COLOR)
gdb_assert_not_reached ("get_rgb called on invalid color");
else if (0 <= m_value && m_value <= 15)
- memcpy (rgb, palette_16colors[m_value], 3 * sizeof (uint8_t));
+ rgb = palette_16colors[m_value];
else if (m_value >= 16 && m_value <= 231)
{
int value = m_value;
@@ -202,12 +199,12 @@ ui_file_style::color::get_rgb (uint8_t *rgb) const
else if (232 <= m_value && m_value <= 255)
{
uint8_t v = (m_value - 232) * 10 + 8;
- rgb[0] = v;
- rgb[1] = v;
- rgb[2] = v;
+ rgb = rgb_color (v, v, v);
}
else
gdb_assert_not_reached ("get_rgb called on invalid color");
+
+ return rgb;
}
/* See ui-style.h. */
@@ -227,9 +224,7 @@ ui_file_style::color::approximate (const std::vector<color_space> &spaces) const
if (target_space == color_space::RGB_24BIT)
{
- uint8_t rgb[3];
- get_rgb (rgb);
- return color (rgb[0], rgb[1], rgb[2]);
+ return color (get_rgb ());
}
int target_size = 0;
@@ -251,14 +246,12 @@ ui_file_style::color::approximate (const std::vector<color_space> &spaces) const
color result = NONE;
int best_distance = std::numeric_limits<int>::max ();
- uint8_t rgb[3];
- get_rgb (rgb);
+ rgb_color rgb = get_rgb ();
for (int i = 0; i < target_size; ++i)
{
- uint8_t c_rgb[3];
color c (target_space, i);
- c.get_rgb (c_rgb);
+ rgb_color c_rgb = c.get_rgb ();
int d_red = std::abs (rgb[0] - c_rgb[0]);
int d_green = std::abs (rgb[1] - c_rgb[1]);
int d_blue = std::abs (rgb[2] - c_rgb[2]);
diff --git a/gdb/ui-style.h b/gdb/ui-style.h
index fca9150889b..a1d656cd39c 100644
--- a/gdb/ui-style.h
+++ b/gdb/ui-style.h
@@ -54,6 +54,33 @@ extern bool color_space_safe_cast (color_space *result, long c);
/* Get the number of colors supported by the terminal where GDB is running. */
extern int gdb_get_ncolors ();
+/* Convenient wrapper for RGB color values. */
+struct rgb_color
+{
+private:
+ std::array <uint8_t, 3> m_data;
+
+public:
+ constexpr rgb_color ()
+ : m_data {}
+ {}
+ constexpr rgb_color (uint8_t r, uint8_t g, uint8_t b)
+ : m_data {r, g, b}
+ {}
+
+ constexpr uint8_t r () const noexcept { return m_data[0]; }
+ constexpr uint8_t g () const noexcept { return m_data[1]; }
+ constexpr uint8_t b () const noexcept { return m_data[2]; }
+
+ constexpr operator uint8_t *() noexcept { return m_data.data (); }
+ constexpr size_t size () const noexcept { return m_data.size (); }
+
+ constexpr uint8_t &operator[] (std::size_t idx) noexcept
+ { return m_data[idx]; }
+ constexpr const uint8_t &operator[] (std::size_t idx) const noexcept
+ { return m_data[idx]; }
+};
+
/* Styles that can be applied to a ui_file. */
struct ui_file_style
{
@@ -131,6 +158,14 @@ struct ui_file_style
c, range.first, range.second, static_cast<int> (cs));
}
+ explicit color (const rgb_color &rgb)
+ : m_color_space (color_space::RGB_24BIT),
+ m_red (rgb.r ()),
+ m_green (rgb.g ()),
+ m_blue (rgb.b ())
+ {
+ }
+
color (uint8_t r, uint8_t g, uint8_t b)
: m_color_space (color_space::RGB_24BIT),
m_red (r),
@@ -216,7 +251,7 @@ struct ui_file_style
/* Fill in RGB with the red/green/blue values for this color.
This may not be called for basic colors or for the "NONE"
color. */
- void get_rgb (uint8_t *rgb) const;
+ rgb_color get_rgb () const;
/* Append the ANSI terminal escape sequence for this color to STR.
IS_FG indicates whether this is a foreground or background
diff --git a/gdb/unittests/style-selftests.c b/gdb/unittests/style-selftests.c
index f10a24d4217..9035050bd6c 100644
--- a/gdb/unittests/style-selftests.c
+++ b/gdb/unittests/style-selftests.c
@@ -31,7 +31,7 @@ run_tests ()
{
ui_file_style style;
size_t n_read;
- uint8_t rgb[3];
+ rgb_color rgb;
SELF_CHECK (style.parse ("\033[m", &n_read));
SELF_CHECK (n_read == 3);
@@ -94,10 +94,10 @@ run_tests ()
SELF_CHECK (style.parse ("\033[38;5;112;48;5;249m", &n_read));
SELF_CHECK (n_read == 20);
SELF_CHECK (!style.get_foreground ().is_basic ());
- style.get_foreground ().get_rgb (rgb);
+ rgb = style.get_foreground ().get_rgb ();
CHECK_RGB (0x87, 0xd7, 0);
SELF_CHECK (!style.get_background ().is_basic ());
- style.get_background ().get_rgb (rgb);
+ rgb = style.get_background ().get_rgb ();
CHECK_RGB (0xb2, 0xb2, 0xb2);
SELF_CHECK (style.get_intensity () == ui_file_style::NORMAL);
SELF_CHECK (!style.is_italic ());
@@ -109,10 +109,10 @@ run_tests ()
SELF_CHECK (style.parse ("\033[38;2;83;84;85;48;2;0;1;254;2;7m", &n_read));
SELF_CHECK (n_read == 33);
SELF_CHECK (!style.get_foreground ().is_basic ());
- style.get_foreground ().get_rgb (rgb);
+ rgb = style.get_foreground ().get_rgb ();
CHECK_RGB (83, 84, 85);
SELF_CHECK (!style.get_background ().is_basic ());
- style.get_background ().get_rgb (rgb);
+ rgb = style.get_background ().get_rgb ();
CHECK_RGB (0, 1, 254);
SELF_CHECK (style.get_intensity () == ui_file_style::DIM);
SELF_CHECK (!style.is_italic ());
--
2.53.0
^ permalink raw reply [flat|nested] 25+ messages in thread* Re: [PATCH v3 1/7] gdb: introduce rgb_color type to simplify existing code
2026-03-09 17:56 ` [PATCH v3 1/7] gdb: introduce rgb_color type to simplify existing code Matthieu Longo
@ 2026-03-09 19:22 ` Tom Tromey
2026-03-11 15:03 ` Simon Marchi
0 siblings, 1 reply; 25+ messages in thread
From: Tom Tromey @ 2026-03-09 19:22 UTC (permalink / raw)
To: Matthieu Longo; +Cc: gdb-patches, Tom Tromey
>>>>> Matthieu Longo <matthieu.longo@arm.com> writes:
> This patch replaces the raw uint8[3] buffer used to represent RGB values
> with a more convenient wrapper, rgb_color, around std::array<uint8_t, 3>.
> It also changes the return type of ui_file_style::color::get_rgb to
> rgb_color instead of filling a caller-provided buffer, and updates all
> callers accordingly.
One tiny nit.
> if (target_space == color_space::RGB_24BIT)
> {
> - uint8_t rgb[3];
> - get_rgb (rgb);
> - return color (rgb[0], rgb[1], rgb[2]);
> + return color (get_rgb ());
> }
Since there's only one statement now, the braces should be removed.
This is ok with this change.
Approved-By: Tom Tromey <tom@tromey.com>
Tom
^ permalink raw reply [flat|nested] 25+ messages in thread* Re: [PATCH v3 1/7] gdb: introduce rgb_color type to simplify existing code
2026-03-09 19:22 ` Tom Tromey
@ 2026-03-11 15:03 ` Simon Marchi
2026-03-11 17:55 ` Matthieu Longo
0 siblings, 1 reply; 25+ messages in thread
From: Simon Marchi @ 2026-03-11 15:03 UTC (permalink / raw)
To: Tom Tromey, Matthieu Longo; +Cc: gdb-patches
On 3/9/26 3:22 PM, Tom Tromey wrote:
>>>>>> Matthieu Longo <matthieu.longo@arm.com> writes:
>
>> This patch replaces the raw uint8[3] buffer used to represent RGB values
>> with a more convenient wrapper, rgb_color, around std::array<uint8_t, 3>.
>> It also changes the return type of ui_file_style::color::get_rgb to
>> rgb_color instead of filling a caller-provided buffer, and updates all
>> callers accordingly.
>
> One tiny nit.
>
>> if (target_space == color_space::RGB_24BIT)
>> {
>> - uint8_t rgb[3];
>> - get_rgb (rgb);
>> - return color (rgb[0], rgb[1], rgb[2]);
>> + return color (get_rgb ());
>> }
>
> Since there's only one statement now, the braces should be removed.
>
> This is ok with this change.
> Approved-By: Tom Tromey <tom@tromey.com>
>
> Tom
This appears to have caused a build failure on armhf:
https://builder.sourceware.org/buildbot/#/builders/72/builds/7402/steps/4/logs/stdio
Sample:
../../binutils-gdb/gdb/tui/tui-io.c: In function ‘bool get_color(const ui_file_style::color&, int*)’:
../../binutils-gdb/gdb/tui/tui-io.c:260:38: error: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second: [-Werror]
260 | if (init_color (next, rgb[0] * 1000 / 255, rgb[1] * 1000 / 255,
| ^
In file included from ./../../binutils-gdb/gdb/ui-file.h:23,
from ./../../binutils-gdb/gdb/defs.h:60,
from <command-line>:
./../../binutils-gdb/gdb/ui-style.h:78:22: note: candidate 1: ‘constexpr uint8_t& rgb_color::operator[](std::size_t)’
78 | constexpr uint8_t &operator[] (std::size_t idx) noexcept
| ^~~~~~~~
../../binutils-gdb/gdb/tui/tui-io.c:260:38: note: candidate 2: ‘operator[](uint8_t* {aka unsigned char*}, int)’ (built-in)
260 | if (init_color (next, rgb[0] * 1000 / 255, rgb[1] * 1000 / 255,
| ^
It seems to be because an rgb_color can be implicitly converted to a
`uint8_t *`, which can be subscripted, but also has an operator[].
Not sure why the uint8_t* operator is needed, but it compiles fine here
if I remove it. If really needed, I think that an explicit method
(`.data()`) would be preferable, to avoid unwanted conversions.
Simon
^ permalink raw reply [flat|nested] 25+ messages in thread* Re: [PATCH v3 1/7] gdb: introduce rgb_color type to simplify existing code
2026-03-11 15:03 ` Simon Marchi
@ 2026-03-11 17:55 ` Matthieu Longo
2026-03-11 18:04 ` Simon Marchi
2026-03-11 18:16 ` Tom Tromey
0 siblings, 2 replies; 25+ messages in thread
From: Matthieu Longo @ 2026-03-11 17:55 UTC (permalink / raw)
To: Simon Marchi, Tom Tromey; +Cc: gdb-patches
On 11/03/2026 15:03, Simon Marchi wrote:
> On 3/9/26 3:22 PM, Tom Tromey wrote:
>>>>>>> Matthieu Longo <matthieu.longo@arm.com> writes:
>>
>>> This patch replaces the raw uint8[3] buffer used to represent RGB values
>>> with a more convenient wrapper, rgb_color, around std::array<uint8_t, 3>.
>>> It also changes the return type of ui_file_style::color::get_rgb to
>>> rgb_color instead of filling a caller-provided buffer, and updates all
>>> callers accordingly.
>>
>> One tiny nit.
>>
>>> if (target_space == color_space::RGB_24BIT)
>>> {
>>> - uint8_t rgb[3];
>>> - get_rgb (rgb);
>>> - return color (rgb[0], rgb[1], rgb[2]);
>>> + return color (get_rgb ());
>>> }
>>
>> Since there's only one statement now, the braces should be removed.
>>
>> This is ok with this change.
>> Approved-By: Tom Tromey <tom@tromey.com>
>>
>> Tom
>
> This appears to have caused a build failure on armhf:
>
> https://builder.sourceware.org/buildbot/#/builders/72/builds/7402/steps/4/logs/stdio
>
> Sample:
>
> ../../binutils-gdb/gdb/tui/tui-io.c: In function ‘bool get_color(const ui_file_style::color&, int*)’:
> ../../binutils-gdb/gdb/tui/tui-io.c:260:38: error: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second: [-Werror]
> 260 | if (init_color (next, rgb[0] * 1000 / 255, rgb[1] * 1000 / 255,
> | ^
> In file included from ./../../binutils-gdb/gdb/ui-file.h:23,
> from ./../../binutils-gdb/gdb/defs.h:60,
> from <command-line>:
> ./../../binutils-gdb/gdb/ui-style.h:78:22: note: candidate 1: ‘constexpr uint8_t& rgb_color::operator[](std::size_t)’
> 78 | constexpr uint8_t &operator[] (std::size_t idx) noexcept
> | ^~~~~~~~
> ../../binutils-gdb/gdb/tui/tui-io.c:260:38: note: candidate 2: ‘operator[](uint8_t* {aka unsigned char*}, int)’ (built-in)
> 260 | if (init_color (next, rgb[0] * 1000 / 255, rgb[1] * 1000 / 255,
> | ^
>
> It seems to be because an rgb_color can be implicitly converted to a
> `uint8_t *`, which can be subscripted, but also has an operator[].
>
> Not sure why the uint8_t* operator is needed,
It was originally required for this line when passing rgb to memcpy().
memcpy (rgb, palette_8colors[m_value], rgb.bytes_size ());
But was later replaced by this assignment:
rgb = palette_8colors[m_value];
> but it compiles fine here if I remove it. If really needed, I think that an explicit method
> (`.data()`) would be preferable, to avoid unwanted conversions.
>
> Simon
I locally tested the removal of this uint8_t* operator, and the code compiles fine.
diff --git a/gdb/ui-style.h b/gdb/ui-style.h
index a1d656cd39c..fc40b93709d 100644
--- a/gdb/ui-style.h
+++ b/gdb/ui-style.h
@@ -72,7 +72,6 @@ struct rgb_color
constexpr uint8_t g () const noexcept { return m_data[1]; }
constexpr uint8_t b () const noexcept { return m_data[2]; }
- constexpr operator uint8_t *() noexcept { return m_data.data (); }
constexpr size_t size () const noexcept { return m_data.size (); }
constexpr uint8_t &operator[] (std::size_t idx) noexcept
Do you want me to publish a patch for this ? Or will you fix it ?
Matthieu
^ permalink raw reply [flat|nested] 25+ messages in thread* Re: [PATCH v3 1/7] gdb: introduce rgb_color type to simplify existing code
2026-03-11 17:55 ` Matthieu Longo
@ 2026-03-11 18:04 ` Simon Marchi
2026-03-11 18:16 ` Tom Tromey
1 sibling, 0 replies; 25+ messages in thread
From: Simon Marchi @ 2026-03-11 18:04 UTC (permalink / raw)
To: Matthieu Longo, Tom Tromey; +Cc: gdb-patches
On 3/11/26 1:55 PM, Matthieu Longo wrote:
> On 11/03/2026 15:03, Simon Marchi wrote:
>> On 3/9/26 3:22 PM, Tom Tromey wrote:
>>>>>>>> Matthieu Longo <matthieu.longo@arm.com> writes:
>>>
>>>> This patch replaces the raw uint8[3] buffer used to represent RGB values
>>>> with a more convenient wrapper, rgb_color, around std::array<uint8_t, 3>.
>>>> It also changes the return type of ui_file_style::color::get_rgb to
>>>> rgb_color instead of filling a caller-provided buffer, and updates all
>>>> callers accordingly.
>>>
>>> One tiny nit.
>>>
>>>> if (target_space == color_space::RGB_24BIT)
>>>> {
>>>> - uint8_t rgb[3];
>>>> - get_rgb (rgb);
>>>> - return color (rgb[0], rgb[1], rgb[2]);
>>>> + return color (get_rgb ());
>>>> }
>>> Since there's only one statement now, the braces should be removed.
>>>
>>> This is ok with this change.
>>> Approved-By: Tom Tromey <tom@tromey.com>
>>>
>>> Tom
>>
>> This appears to have caused a build failure on armhf:
>>
>> https://builder.sourceware.org/buildbot/#/builders/72/builds/7402/steps/4/logs/stdio
>>
>> Sample:
>>
>> ../../binutils-gdb/gdb/tui/tui-io.c: In function ‘bool get_color(const ui_file_style::color&, int*)’:
>> ../../binutils-gdb/gdb/tui/tui-io.c:260:38: error: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second: [-Werror]
>> 260 | if (init_color (next, rgb[0] * 1000 / 255, rgb[1] * 1000 / 255,
>> | ^
>> In file included from ./../../binutils-gdb/gdb/ui-file.h:23,
>> from ./../../binutils-gdb/gdb/defs.h:60,
>> from <command-line>:
>> ./../../binutils-gdb/gdb/ui-style.h:78:22: note: candidate 1: ‘constexpr uint8_t& rgb_color::operator[](std::size_t)’
>> 78 | constexpr uint8_t &operator[] (std::size_t idx) noexcept
>> | ^~~~~~~~
>> ../../binutils-gdb/gdb/tui/tui-io.c:260:38: note: candidate 2: ‘operator[](uint8_t* {aka unsigned char*}, int)’ (built-in)
>> 260 | if (init_color (next, rgb[0] * 1000 / 255, rgb[1] * 1000 / 255,
>> | ^
>>
>> It seems to be because an rgb_color can be implicitly converted to a
>> `uint8_t *`, which can be subscripted, but also has an operator[].
>>
>> Not sure why the uint8_t* operator is needed,
>
> It was originally required for this line when passing rgb to memcpy().
>
> memcpy (rgb, palette_8colors[m_value], rgb.bytes_size ());
>
> But was later replaced by this assignment:
>
> rgb = palette_8colors[m_value];
>
>> but it compiles fine here if I remove it. If really needed, I think that an explicit method
>> (`.data()`) would be preferable, to avoid unwanted conversions.
>>
>> Simon
>
> I locally tested the removal of this uint8_t* operator, and the code compiles fine.
>
> diff --git a/gdb/ui-style.h b/gdb/ui-style.h
> index a1d656cd39c..fc40b93709d 100644
> --- a/gdb/ui-style.h
> +++ b/gdb/ui-style.h
> @@ -72,7 +72,6 @@ struct rgb_color
> constexpr uint8_t g () const noexcept { return m_data[1]; }
> constexpr uint8_t b () const noexcept { return m_data[2]; }
>
> - constexpr operator uint8_t *() noexcept { return m_data.data (); }
> constexpr size_t size () const noexcept { return m_data.size (); }
>
> constexpr uint8_t &operator[] (std::size_t idx) noexcept
>
> Do you want me to publish a patch for this ? Or will you fix it ?
Please push a patch that does exactly that. You can put my:
Approved-By: Simon Marchi <simon.marchi@efficios.com>
Simon
^ permalink raw reply [flat|nested] 25+ messages in thread* Re: [PATCH v3 1/7] gdb: introduce rgb_color type to simplify existing code
2026-03-11 17:55 ` Matthieu Longo
2026-03-11 18:04 ` Simon Marchi
@ 2026-03-11 18:16 ` Tom Tromey
1 sibling, 0 replies; 25+ messages in thread
From: Tom Tromey @ 2026-03-11 18:16 UTC (permalink / raw)
To: Matthieu Longo; +Cc: Simon Marchi, Tom Tromey, gdb-patches
>>>>> Matthieu Longo <matthieu.longo@arm.com> writes:
>> It seems to be because an rgb_color can be implicitly converted to a
>> `uint8_t *`, which can be subscripted, but also has an operator[].
>> Not sure why the uint8_t* operator is needed,
First, sorry about that, I missed the implicit conversion in my review.
While I think those can be ok sometimes, most of the times they aren't.
> rgb = palette_8colors[m_value];
>> but it compiles fine here if I remove it. If really needed, I think that an explicit method
>> (`.data()`) would be preferable, to avoid unwanted conversions.
> I locally tested the removal of this uint8_t* operator, and the code compiles fine.
> Do you want me to publish a patch for this ? Or will you fix it ?
Please send it as a patch with an appropriate commit message.
It's pre-approved by me. Though TBH this could probably also be obvious
considering it's a one-line removal and fixes the build.
You can add the tag -
Approved-By: Tom Tromey <tom@tromey.com>
thank you,
Tom
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH v3 2/7] gdb: fail configure if Python version is too old for limited API
2026-03-09 17:56 [PATCH v3 0/7] gdb: more fixes for Python limited C API support Matthieu Longo
2026-03-09 17:56 ` [PATCH v3 1/7] gdb: introduce rgb_color type to simplify existing code Matthieu Longo
@ 2026-03-09 17:56 ` Matthieu Longo
2026-03-24 18:53 ` Tom Tromey
2026-03-09 17:56 ` [PATCH v3 3/7] gdb: add new helpers for retrieving a type's fully qualified name Matthieu Longo
` (5 subsequent siblings)
7 siblings, 1 reply; 25+ messages in thread
From: Matthieu Longo @ 2026-03-09 17:56 UTC (permalink / raw)
To: gdb-patches, Tom Tromey; +Cc: Matthieu Longo
GDB can be built against the Python limited API using the configure
flag '--enable-py-limited-api=yes'. This flag is currently experimental,
and the build is not yet fully successful. Today, the minimum required
Python version for this option is 3.11. This requirement is not final
and will be raised to a later version as the migration progresses.
However, the configure script does not currently report an error if an
older version of Python is used. Instead, the build fails later with
numerous errors that are difficult to relate to Python limited API
compatiblity.
This patch adds a version check when '--enable-py-limited-api=yes' is
specified, ensuring that the provided Python version meets the minimum
requirements for the limited API support. If it does not, configure will
now fail with a clear error message.
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=23830
---
gdb/aclocal.m4 | 253 +++++++++++++++++++++++++++++++++++++++++++++++
gdb/configure | 27 ++++-
gdb/configure.ac | 20 +++-
3 files changed, 294 insertions(+), 6 deletions(-)
diff --git a/gdb/aclocal.m4 b/gdb/aclocal.m4
index cae09708e01..254cb4edadc 100644
--- a/gdb/aclocal.m4
+++ b/gdb/aclocal.m4
@@ -154,6 +154,259 @@ AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles])
]
)
+# Copyright (C) 1999-2017 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+
+# AM_PATH_PYTHON([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+# ---------------------------------------------------------------------------
+# Adds support for distributing Python modules and packages. To
+# install modules, copy them to $(pythondir), using the python_PYTHON
+# automake variable. To install a package with the same name as the
+# automake package, install to $(pkgpythondir), or use the
+# pkgpython_PYTHON automake variable.
+#
+# The variables $(pyexecdir) and $(pkgpyexecdir) are provided as
+# locations to install python extension modules (shared libraries).
+# Another macro is required to find the appropriate flags to compile
+# extension modules.
+#
+# If your package is configured with a different prefix to python,
+# users will have to add the install directory to the PYTHONPATH
+# environment variable, or create a .pth file (see the python
+# documentation for details).
+#
+# If the MINIMUM-VERSION argument is passed, AM_PATH_PYTHON will
+# cause an error if the version of python installed on the system
+# doesn't meet the requirement. MINIMUM-VERSION should consist of
+# numbers and dots only.
+AC_DEFUN([AM_PATH_PYTHON],
+ [
+ dnl Find a Python interpreter. Python versions prior to 2.0 are not
+ dnl supported. (2.0 was released on October 16, 2000).
+ dnl FIXME: Remove the need to hard-code Python versions here.
+ m4_define_default([_AM_PYTHON_INTERPRETER_LIST],
+[python python2 python3 python3.5 python3.4 python3.3 python3.2 python3.1 python3.0 python2.7 dnl
+ python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python2.0])
+
+ AC_ARG_VAR([PYTHON], [the Python interpreter])
+
+ m4_if([$1],[],[
+ dnl No version check is needed.
+ # Find any Python interpreter.
+ if test -z "$PYTHON"; then
+ AC_PATH_PROGS([PYTHON], _AM_PYTHON_INTERPRETER_LIST, :)
+ fi
+ am_display_PYTHON=python
+ ], [
+ dnl A version check is needed.
+ if test -n "$PYTHON"; then
+ # If the user set $PYTHON, use it and don't search something else.
+ AC_MSG_CHECKING([whether $PYTHON version is >= $1])
+ AM_PYTHON_CHECK_VERSION([$PYTHON], [$1],
+ [AC_MSG_RESULT([yes])],
+ [AC_MSG_RESULT([no])
+ AC_MSG_ERROR([Python interpreter is too old])])
+ am_display_PYTHON=$PYTHON
+ else
+ # Otherwise, try each interpreter until we find one that satisfies
+ # VERSION.
+ AC_CACHE_CHECK([for a Python interpreter with version >= $1],
+ [am_cv_pathless_PYTHON],[
+ for am_cv_pathless_PYTHON in _AM_PYTHON_INTERPRETER_LIST none; do
+ test "$am_cv_pathless_PYTHON" = none && break
+ AM_PYTHON_CHECK_VERSION([$am_cv_pathless_PYTHON], [$1], [break])
+ done])
+ # Set $PYTHON to the absolute path of $am_cv_pathless_PYTHON.
+ if test "$am_cv_pathless_PYTHON" = none; then
+ PYTHON=:
+ else
+ AC_PATH_PROG([PYTHON], [$am_cv_pathless_PYTHON])
+ fi
+ am_display_PYTHON=$am_cv_pathless_PYTHON
+ fi
+ ])
+
+ if test "$PYTHON" = :; then
+ dnl Run any user-specified action, or abort.
+ m4_default([$3], [AC_MSG_ERROR([no suitable Python interpreter found])])
+ else
+
+ dnl Query Python for its version number. Getting [:3] seems to be
+ dnl the best way to do this; it's what "site.py" does in the standard
+ dnl library.
+
+ AC_CACHE_CHECK([for $am_display_PYTHON version], [am_cv_python_version],
+ [am_cv_python_version=`$PYTHON -c "import sys; sys.stdout.write(sys.version[[:3]])"`])
+ AC_SUBST([PYTHON_VERSION], [$am_cv_python_version])
+
+ dnl Use the values of $prefix and $exec_prefix for the corresponding
+ dnl values of PYTHON_PREFIX and PYTHON_EXEC_PREFIX. These are made
+ dnl distinct variables so they can be overridden if need be. However,
+ dnl general consensus is that you shouldn't need this ability.
+
+ AC_SUBST([PYTHON_PREFIX], ['${prefix}'])
+ AC_SUBST([PYTHON_EXEC_PREFIX], ['${exec_prefix}'])
+
+ dnl At times (like when building shared libraries) you may want
+ dnl to know which OS platform Python thinks this is.
+
+ AC_CACHE_CHECK([for $am_display_PYTHON platform], [am_cv_python_platform],
+ [am_cv_python_platform=`$PYTHON -c "import sys; sys.stdout.write(sys.platform)"`])
+ AC_SUBST([PYTHON_PLATFORM], [$am_cv_python_platform])
+
+ # Just factor out some code duplication.
+ am_python_setup_sysconfig="\
+import sys
+# Prefer sysconfig over distutils.sysconfig, for better compatibility
+# with python 3.x. See automake bug#10227.
+try:
+ import sysconfig
+except ImportError:
+ can_use_sysconfig = 0
+else:
+ can_use_sysconfig = 1
+# Can't use sysconfig in CPython 2.7, since it's broken in virtualenvs:
+# <https://github.com/pypa/virtualenv/issues/118>
+try:
+ from platform import python_implementation
+ if python_implementation() == 'CPython' and sys.version[[:3]] == '2.7':
+ can_use_sysconfig = 0
+except ImportError:
+ pass"
+
+ dnl Set up 4 directories:
+
+ dnl pythondir -- where to install python scripts. This is the
+ dnl site-packages directory, not the python standard library
+ dnl directory like in previous automake betas. This behavior
+ dnl is more consistent with lispdir.m4 for example.
+ dnl Query distutils for this directory.
+ AC_CACHE_CHECK([for $am_display_PYTHON script directory],
+ [am_cv_python_pythondir],
+ [if test "x$prefix" = xNONE
+ then
+ am_py_prefix=$ac_default_prefix
+ else
+ am_py_prefix=$prefix
+ fi
+ am_cv_python_pythondir=`$PYTHON -c "
+$am_python_setup_sysconfig
+if can_use_sysconfig:
+ sitedir = sysconfig.get_path('purelib', vars={'base':'$am_py_prefix'})
+else:
+ from distutils import sysconfig
+ sitedir = sysconfig.get_python_lib(0, 0, prefix='$am_py_prefix')
+sys.stdout.write(sitedir)"`
+ case $am_cv_python_pythondir in
+ $am_py_prefix*)
+ am__strip_prefix=`echo "$am_py_prefix" | sed 's|.|.|g'`
+ am_cv_python_pythondir=`echo "$am_cv_python_pythondir" | sed "s,^$am__strip_prefix,$PYTHON_PREFIX,"`
+ ;;
+ *)
+ case $am_py_prefix in
+ /usr|/System*) ;;
+ *)
+ am_cv_python_pythondir=$PYTHON_PREFIX/lib/python$PYTHON_VERSION/site-packages
+ ;;
+ esac
+ ;;
+ esac
+ ])
+ AC_SUBST([pythondir], [$am_cv_python_pythondir])
+
+ dnl pkgpythondir -- $PACKAGE directory under pythondir. Was
+ dnl PYTHON_SITE_PACKAGE in previous betas, but this naming is
+ dnl more consistent with the rest of automake.
+
+ AC_SUBST([pkgpythondir], [\${pythondir}/$PACKAGE])
+
+ dnl pyexecdir -- directory for installing python extension modules
+ dnl (shared libraries)
+ dnl Query distutils for this directory.
+ AC_CACHE_CHECK([for $am_display_PYTHON extension module directory],
+ [am_cv_python_pyexecdir],
+ [if test "x$exec_prefix" = xNONE
+ then
+ am_py_exec_prefix=$am_py_prefix
+ else
+ am_py_exec_prefix=$exec_prefix
+ fi
+ am_cv_python_pyexecdir=`$PYTHON -c "
+$am_python_setup_sysconfig
+if can_use_sysconfig:
+ sitedir = sysconfig.get_path('platlib', vars={'platbase':'$am_py_prefix'})
+else:
+ from distutils import sysconfig
+ sitedir = sysconfig.get_python_lib(1, 0, prefix='$am_py_prefix')
+sys.stdout.write(sitedir)"`
+ case $am_cv_python_pyexecdir in
+ $am_py_exec_prefix*)
+ am__strip_prefix=`echo "$am_py_exec_prefix" | sed 's|.|.|g'`
+ am_cv_python_pyexecdir=`echo "$am_cv_python_pyexecdir" | sed "s,^$am__strip_prefix,$PYTHON_EXEC_PREFIX,"`
+ ;;
+ *)
+ case $am_py_exec_prefix in
+ /usr|/System*) ;;
+ *)
+ am_cv_python_pyexecdir=$PYTHON_EXEC_PREFIX/lib/python$PYTHON_VERSION/site-packages
+ ;;
+ esac
+ ;;
+ esac
+ ])
+ AC_SUBST([pyexecdir], [$am_cv_python_pyexecdir])
+
+ dnl pkgpyexecdir -- $(pyexecdir)/$(PACKAGE)
+
+ AC_SUBST([pkgpyexecdir], [\${pyexecdir}/$PACKAGE])
+
+ dnl Run any user-specified action.
+ $2
+ fi
+
+])
+
+
+# AM_PYTHON_CHECK_VERSION(PROG, VERSION, [ACTION-IF-TRUE], [ACTION-IF-FALSE])
+# ---------------------------------------------------------------------------
+# Run ACTION-IF-TRUE if the Python interpreter PROG has version >= VERSION.
+# Run ACTION-IF-FALSE otherwise.
+# This test uses sys.hexversion instead of the string equivalent (first
+# word of sys.version), in order to cope with versions such as 2.2c1.
+# This supports Python 2.0 or higher. (2.0 was released on October 16, 2000).
+AC_DEFUN([AM_PYTHON_CHECK_VERSION],
+ [prog="import sys
+# split strings by '.' and convert to numeric. Append some zeros
+# because we need at least 4 digits for the hex conversion.
+# map returns an iterator in Python 3.0 and a list in 2.x
+minver = list(map(int, '$2'.split('.'))) + [[0, 0, 0]]
+minverhex = 0
+# xrange is not present in Python 3.0 and range returns an iterator
+for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[[i]]
+sys.exit(sys.hexversion < minverhex)"
+ AS_IF([AM_RUN_LOG([$1 -c "$prog"])], [$3], [$4])])
+
+# Copyright (C) 2001-2017 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_RUN_LOG(COMMAND)
+# -------------------
+# Run COMMAND, save the exit status in ac_status, and log it.
+# (This has been adapted from Autoconf's _AC_RUN_LOG macro.)
+AC_DEFUN([AM_RUN_LOG],
+[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD
+ ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+ (exit $ac_status); }])
+
# Copyright (C) 2001-2017 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
diff --git a/gdb/configure b/gdb/configure
index 12c54521682..dcd5c030713 100755
--- a/gdb/configure
+++ b/gdb/configure
@@ -28507,6 +28507,7 @@ fi
# do except assume that the compiler will be able to find those files.
python_includes=
python_libs=
+ python_prefix=
have_python_config=no
fi
@@ -28743,6 +28744,7 @@ else
fi
+
# Check whether to build GDB against Python limited C API.
# Check whether --enable-py-limited-api was given.
if test "${enable_py_limited_api+set}" = set; then :
@@ -28763,11 +28765,32 @@ fi
if test "$enable_py_limited_api" = yes; then
# The minimal Python limited API version is currently set to 3.11 for the
# support of PyBuffer_FillInfo and PyBuffer_Release.
- # The choice of the minimal version for the Python limited API won't be frozen
- # until the end of the migration.
+ # The choice of the minimal version for the Python limited API won't be
+ # frozen until the end of the migration.
+ prog="import sys
+# split strings by '.' and convert to numeric. Append some zeros
+# because we need at least 4 digits for the hex conversion.
+# map returns an iterator in Python 3.0 and a list in 2.x
+minver = list(map(int, '3.11'.split('.'))) + [0, 0, 0]
+minverhex = 0
+# xrange is not present in Python 3.0 and range returns an iterator
+for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[i]
+sys.exit(sys.hexversion < minverhex)"
+ if { echo "$as_me:$LINENO: $python_prog -c "$prog"" >&5
+ ($python_prog -c "$prog") >&5 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then :
+
$as_echo "#define Py_LIMITED_API 0x030b0000" >>confdefs.h
+
+else
+
+ as_fn_error $? "Python limited API support requires at least Python version 3.11" "$LINENO" 5
+
+fi
fi
# -------------------- #
diff --git a/gdb/configure.ac b/gdb/configure.ac
index cf8078e1d89..17f648d0b49 100644
--- a/gdb/configure.ac
+++ b/gdb/configure.ac
@@ -933,6 +933,7 @@ else
# do except assume that the compiler will be able to find those files.
python_includes=
python_libs=
+ python_prefix=
have_python_config=no
fi
@@ -1062,6 +1063,13 @@ AC_SUBST(PYTHON_CPPFLAGS)
AC_SUBST(PYTHON_LIBS)
AM_CONDITIONAL(HAVE_PYTHON, test "${have_libpython}" != no)
+dnl Use --enable-py-limited-api to enable the build of GDB against the Python
+dnl limited API.
+dnl
+dnl no - Disable the Python limited API.
+dnl yes - Use the Python limited API to build GDB, error if the selected
+dnl version of Python is not compatible with the Python limited API.
+
# Check whether to build GDB against Python limited C API.
AC_ARG_ENABLE([py-limited-api],
[AS_HELP_STRING([--enable-py-limited-api],
@@ -1072,10 +1080,14 @@ AC_ARG_ENABLE([py-limited-api],
if test "$enable_py_limited_api" = yes; then
# The minimal Python limited API version is currently set to 3.11 for the
# support of PyBuffer_FillInfo and PyBuffer_Release.
- # The choice of the minimal version for the Python limited API won't be frozen
- # until the end of the migration.
- AC_DEFINE(Py_LIMITED_API, 0x030b0000,
- [Define if GDB should be built against the Python limited C API.])
+ # The choice of the minimal version for the Python limited API won't be
+ # frozen until the end of the migration.
+ AM_PYTHON_CHECK_VERSION([$python_prog], [3.11], [
+ AC_DEFINE(Py_LIMITED_API, 0x030b0000,
+ [Define if GDB should be built against the Python limited C API.])
+ ],[
+ AC_MSG_ERROR([Python limited API support requires at least Python version 3.11])
+ ])
fi
# -------------------- #
--
2.53.0
^ permalink raw reply [flat|nested] 25+ messages in thread* Re: [PATCH v3 2/7] gdb: fail configure if Python version is too old for limited API
2026-03-09 17:56 ` [PATCH v3 2/7] gdb: fail configure if Python version is too old for limited API Matthieu Longo
@ 2026-03-24 18:53 ` Tom Tromey
2026-03-31 10:16 ` Matthieu Longo
0 siblings, 1 reply; 25+ messages in thread
From: Tom Tromey @ 2026-03-24 18:53 UTC (permalink / raw)
To: Matthieu Longo; +Cc: gdb-patches, Tom Tromey
>>>>> Matthieu Longo <matthieu.longo@arm.com> writes:
> GDB can be built against the Python limited API using the configure
> flag '--enable-py-limited-api=yes'. This flag is currently experimental,
> and the build is not yet fully successful. Today, the minimum required
> Python version for this option is 3.11. This requirement is not final
> and will be raised to a later version as the migration progresses.
> However, the configure script does not currently report an error if an
> older version of Python is used. Instead, the build fails later with
> numerous errors that are difficult to relate to Python limited API
> compatiblity.
> This patch adds a version check when '--enable-py-limited-api=yes' is
> specified, ensuring that the provided Python version meets the minimum
> requirements for the limited API support. If it does not, configure will
> now fail with a clear error message.
Sorry about the delay on this.
I'm a bit worried about adding a bunch of new python-related configury.
Like are we sure it will agree with what's already in there? As in,
picking up the same version from the same place, etc? For instance does
this new code respect --with-python-libdir?
I wonder if instead there could just be a new compile test that is run
when --enable-py-limited-api is used. This test could just check
PY_VERSION_HEX and #error on failure.
Wouldn't that have the same effect but without adding a ton of new code?
TBH I'd even be happy with not doing this check in configure at all and
saying that if you specify --enable-py-limited-api then you should know
what you're doing and if you mess up you'll just get compile-time
errors. That is, stick the check in python-internal.h.
Tom
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v3 2/7] gdb: fail configure if Python version is too old for limited API
2026-03-24 18:53 ` Tom Tromey
@ 2026-03-31 10:16 ` Matthieu Longo
2026-04-07 13:36 ` Matthieu Longo
2026-04-07 20:39 ` Tom Tromey
0 siblings, 2 replies; 25+ messages in thread
From: Matthieu Longo @ 2026-03-31 10:16 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
On 24/03/2026 18:53, Tom Tromey wrote:
>>>>>> Matthieu Longo <matthieu.longo@arm.com> writes:
>
>> GDB can be built against the Python limited API using the configure
>> flag '--enable-py-limited-api=yes'. This flag is currently experimental,
>> and the build is not yet fully successful. Today, the minimum required
>> Python version for this option is 3.11. This requirement is not final
>> and will be raised to a later version as the migration progresses.
>
>> However, the configure script does not currently report an error if an
>> older version of Python is used. Instead, the build fails later with
>> numerous errors that are difficult to relate to Python limited API
>> compatiblity.
>
>> This patch adds a version check when '--enable-py-limited-api=yes' is
>> specified, ensuring that the provided Python version meets the minimum
>> requirements for the limited API support. If it does not, configure will
>> now fail with a clear error message.
>
> Sorry about the delay on this.
>
> I'm a bit worried about adding a bunch of new python-related configury.
> Like are we sure it will agree with what's already in there? As in,
> picking up the same version from the same place, etc? For instance does
> this new code respect --with-python-libdir?
>
> I wonder if instead there could just be a new compile test that is run
> when --enable-py-limited-api is used. This test could just check
> PY_VERSION_HEX and #error on failure.
>
> Wouldn't that have the same effect but without adding a ton of new code?
>
> TBH I'd even be happy with not doing this check in configure at all and
> saying that if you specify --enable-py-limited-api then you should know
> what you're doing and if you mess up you'll just get compile-time
> errors. That is, stick the check in python-internal.h.
>
> Tom
So I followed your advice and added a compile test checking PY_VERSION_HEX and #erroring on failure.
Here below is the new patch that I posted here to avoid a new revision.
Matthieu
diff --git a/gdb/configure b/gdb/configure
index 12c54521682..16580e4af7d 100755
--- a/gdb/configure
+++ b/gdb/configure
@@ -28507,6 +28507,7 @@ fi
# do except assume that the compiler will be able to find those files.
python_includes=
python_libs=
+ python_prefix=
have_python_config=no
fi
@@ -28743,6 +28744,7 @@ else
fi
+
# Check whether to build GDB against Python limited C API.
# Check whether --enable-py-limited-api was given.
if test "${enable_py_limited_api+set}" = set; then :
@@ -28763,11 +28765,46 @@ fi
if test "$enable_py_limited_api" = yes; then
# The minimal Python limited API version is currently set to 3.11 for the
# support of PyBuffer_FillInfo and PyBuffer_Release.
- # The choice of the minimal version for the Python limited API won't be frozen
- # until the end of the migration.
+ # The choice of the minimal version for the Python limited API won't be
+ # frozen until the end of the migration.
+ old_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $PYTHON_CFLAGS"
+ old_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $PYTHON_CPPFLAGS"
+ old_LIBS="$LIBS"
+ LIBS="$LIBS $PYTHON_LIBS"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <Python.h>
+#ifndef PY_VERSION_HEX
+#error "PY_VERSION_HEX is not defined"
+#endif
+#if PY_VERSION_HEX < 0x030b0000
+#error "Python limited API support requires at least Python version 3.11"
+#endif
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
$as_echo "#define Py_LIMITED_API 0x030b0000" >>confdefs.h
+
+else
+
+ as_fn_error $? "Python limited API support requires at least Python version 3.11" "$LINENO" 5
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ CFLAGS="$old_CFLAGS"
+ CPPFLAGS="$old_CPPFLAGS"
+ LIBS="$old_LIBS"
fi
# -------------------- #
diff --git a/gdb/configure.ac b/gdb/configure.ac
index cf8078e1d89..c6b0653e3b0 100644
--- a/gdb/configure.ac
+++ b/gdb/configure.ac
@@ -933,6 +933,7 @@ else
# do except assume that the compiler will be able to find those files.
python_includes=
python_libs=
+ python_prefix=
have_python_config=no
fi
@@ -1062,6 +1063,13 @@ AC_SUBST(PYTHON_CPPFLAGS)
AC_SUBST(PYTHON_LIBS)
AM_CONDITIONAL(HAVE_PYTHON, test "${have_libpython}" != no)
+dnl Use --enable-py-limited-api to enable the build of GDB against the Python
+dnl limited API.
+dnl
+dnl no - Disable the Python limited API.
+dnl yes - Use the Python limited API to build GDB, error if the selected
+dnl version of Python is not compatible with the Python limited API.
+
# Check whether to build GDB against Python limited C API.
AC_ARG_ENABLE([py-limited-api],
[AS_HELP_STRING([--enable-py-limited-api],
@@ -1072,10 +1080,31 @@ AC_ARG_ENABLE([py-limited-api],
if test "$enable_py_limited_api" = yes; then
# The minimal Python limited API version is currently set to 3.11 for the
# support of PyBuffer_FillInfo and PyBuffer_Release.
- # The choice of the minimal version for the Python limited API won't be frozen
- # until the end of the migration.
- AC_DEFINE(Py_LIMITED_API, 0x030b0000,
- [Define if GDB should be built against the Python limited C API.])
+ # The choice of the minimal version for the Python limited API won't be
+ # frozen until the end of the migration.
+ old_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $PYTHON_CFLAGS"
+ old_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $PYTHON_CPPFLAGS"
+ old_LIBS="$LIBS"
+ LIBS="$LIBS $PYTHON_LIBS"
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+ [[#include <Python.h>
+#ifndef PY_VERSION_HEX
+#error "PY_VERSION_HEX is not defined"
+#endif
+#if PY_VERSION_HEX < 0x030b0000
+#error "Python limited API support requires at least Python version 3.11"
+#endif
+ ]],[[]])],
+ [AC_DEFINE(Py_LIMITED_API, 0x030b0000,
+ [Define if GDB should be built against the Python limited C API.])
+ ],[
+ AC_MSG_ERROR([Python limited API support requires at least Python version 3.11])
+ ])
+ CFLAGS="$old_CFLAGS"
+ CPPFLAGS="$old_CPPFLAGS"
+ LIBS="$old_LIBS"
fi
# -------------------- #
^ permalink raw reply [flat|nested] 25+ messages in thread* Re: [PATCH v3 2/7] gdb: fail configure if Python version is too old for limited API
2026-03-31 10:16 ` Matthieu Longo
@ 2026-04-07 13:36 ` Matthieu Longo
2026-04-07 20:39 ` Tom Tromey
1 sibling, 0 replies; 25+ messages in thread
From: Matthieu Longo @ 2026-04-07 13:36 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
On 31/03/2026 11:16, Matthieu Longo wrote:
> On 24/03/2026 18:53, Tom Tromey wrote:
>>>>>>> Matthieu Longo <matthieu.longo@arm.com> writes:
>>
>>> GDB can be built against the Python limited API using the configure
>>> flag '--enable-py-limited-api=yes'. This flag is currently experimental,
>>> and the build is not yet fully successful. Today, the minimum required
>>> Python version for this option is 3.11. This requirement is not final
>>> and will be raised to a later version as the migration progresses.
>>
>>> However, the configure script does not currently report an error if an
>>> older version of Python is used. Instead, the build fails later with
>>> numerous errors that are difficult to relate to Python limited API
>>> compatiblity.
>>
>>> This patch adds a version check when '--enable-py-limited-api=yes' is
>>> specified, ensuring that the provided Python version meets the minimum
>>> requirements for the limited API support. If it does not, configure will
>>> now fail with a clear error message.
>>
>> Sorry about the delay on this.
>>
>> I'm a bit worried about adding a bunch of new python-related configury.
>> Like are we sure it will agree with what's already in there? As in,
>> picking up the same version from the same place, etc? For instance does
>> this new code respect --with-python-libdir?
>>
>> I wonder if instead there could just be a new compile test that is run
>> when --enable-py-limited-api is used. This test could just check
>> PY_VERSION_HEX and #error on failure.
>>
>> Wouldn't that have the same effect but without adding a ton of new code?
>>
>> TBH I'd even be happy with not doing this check in configure at all and
>> saying that if you specify --enable-py-limited-api then you should know
>> what you're doing and if you mess up you'll just get compile-time
>> errors. That is, stick the check in python-internal.h.
>>
>> Tom
>
> So I followed your advice and added a compile test checking PY_VERSION_HEX and #erroring on failure.
> Here below is the new patch that I posted here to avoid a new revision.
>
> Matthieu
>
>
> diff --git a/gdb/configure b/gdb/configure
> index 12c54521682..16580e4af7d 100755
> --- a/gdb/configure
> +++ b/gdb/configure
> @@ -28507,6 +28507,7 @@ fi
> # do except assume that the compiler will be able to find those files.
> python_includes=
> python_libs=
> + python_prefix=
> have_python_config=no
> fi
>
> @@ -28743,6 +28744,7 @@ else
> fi
>
>
> +
> # Check whether to build GDB against Python limited C API.
> # Check whether --enable-py-limited-api was given.
> if test "${enable_py_limited_api+set}" = set; then :
> @@ -28763,11 +28765,46 @@ fi
> if test "$enable_py_limited_api" = yes; then
> # The minimal Python limited API version is currently set to 3.11 for the
> # support of PyBuffer_FillInfo and PyBuffer_Release.
> - # The choice of the minimal version for the Python limited API won't be frozen
> - # until the end of the migration.
> + # The choice of the minimal version for the Python limited API won't be
> + # frozen until the end of the migration.
> + old_CFLAGS="$CFLAGS"
> + CFLAGS="$CFLAGS $PYTHON_CFLAGS"
> + old_CPPFLAGS="$CPPFLAGS"
> + CPPFLAGS="$CPPFLAGS $PYTHON_CPPFLAGS"
> + old_LIBS="$LIBS"
> + LIBS="$LIBS $PYTHON_LIBS"
> + cat confdefs.h - <<_ACEOF >conftest.$ac_ext
> +/* end confdefs.h. */
> +#include <Python.h>
> +#ifndef PY_VERSION_HEX
> +#error "PY_VERSION_HEX is not defined"
> +#endif
> +#if PY_VERSION_HEX < 0x030b0000
> +#error "Python limited API support requires at least Python version 3.11"
> +#endif
> +
> +int
> +main ()
> +{
> +
> + ;
> + return 0;
> +}
> +_ACEOF
> +if ac_fn_c_try_compile "$LINENO"; then :
>
> $as_echo "#define Py_LIMITED_API 0x030b0000" >>confdefs.h
>
> +
> +else
> +
> + as_fn_error $? "Python limited API support requires at least Python version 3.11" "$LINENO" 5
> +
> +fi
> +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
> + CFLAGS="$old_CFLAGS"
> + CPPFLAGS="$old_CPPFLAGS"
> + LIBS="$old_LIBS"
> fi
>
> # -------------------- #
> diff --git a/gdb/configure.ac b/gdb/configure.ac
> index cf8078e1d89..c6b0653e3b0 100644
> --- a/gdb/configure.ac
> +++ b/gdb/configure.ac
> @@ -933,6 +933,7 @@ else
> # do except assume that the compiler will be able to find those files.
> python_includes=
> python_libs=
> + python_prefix=
> have_python_config=no
> fi
>
> @@ -1062,6 +1063,13 @@ AC_SUBST(PYTHON_CPPFLAGS)
> AC_SUBST(PYTHON_LIBS)
> AM_CONDITIONAL(HAVE_PYTHON, test "${have_libpython}" != no)
>
> +dnl Use --enable-py-limited-api to enable the build of GDB against the Python
> +dnl limited API.
> +dnl
> +dnl no - Disable the Python limited API.
> +dnl yes - Use the Python limited API to build GDB, error if the selected
> +dnl version of Python is not compatible with the Python limited API.
> +
> # Check whether to build GDB against Python limited C API.
> AC_ARG_ENABLE([py-limited-api],
> [AS_HELP_STRING([--enable-py-limited-api],
> @@ -1072,10 +1080,31 @@ AC_ARG_ENABLE([py-limited-api],
> if test "$enable_py_limited_api" = yes; then
> # The minimal Python limited API version is currently set to 3.11 for the
> # support of PyBuffer_FillInfo and PyBuffer_Release.
> - # The choice of the minimal version for the Python limited API won't be frozen
> - # until the end of the migration.
> - AC_DEFINE(Py_LIMITED_API, 0x030b0000,
> - [Define if GDB should be built against the Python limited C API.])
> + # The choice of the minimal version for the Python limited API won't be
> + # frozen until the end of the migration.
> + old_CFLAGS="$CFLAGS"
> + CFLAGS="$CFLAGS $PYTHON_CFLAGS"
> + old_CPPFLAGS="$CPPFLAGS"
> + CPPFLAGS="$CPPFLAGS $PYTHON_CPPFLAGS"
> + old_LIBS="$LIBS"
> + LIBS="$LIBS $PYTHON_LIBS"
> + AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
> + [[#include <Python.h>
> +#ifndef PY_VERSION_HEX
> +#error "PY_VERSION_HEX is not defined"
> +#endif
> +#if PY_VERSION_HEX < 0x030b0000
> +#error "Python limited API support requires at least Python version 3.11"
> +#endif
> + ]],[[]])],
> + [AC_DEFINE(Py_LIMITED_API, 0x030b0000,
> + [Define if GDB should be built against the Python limited C API.])
> + ],[
> + AC_MSG_ERROR([Python limited API support requires at least Python version 3.11])
> + ])
> + CFLAGS="$old_CFLAGS"
> + CPPFLAGS="$old_CPPFLAGS"
> + LIBS="$old_LIBS"
> fi
>
> # -------------------- #
Ping ?
^ permalink raw reply [flat|nested] 25+ messages in thread* Re: [PATCH v3 2/7] gdb: fail configure if Python version is too old for limited API
2026-03-31 10:16 ` Matthieu Longo
2026-04-07 13:36 ` Matthieu Longo
@ 2026-04-07 20:39 ` Tom Tromey
1 sibling, 0 replies; 25+ messages in thread
From: Tom Tromey @ 2026-04-07 20:39 UTC (permalink / raw)
To: Matthieu Longo; +Cc: Tom Tromey, gdb-patches
>>>>> "Matthieu" == Matthieu Longo <matthieu.longo@arm.com> writes:
Matthieu> So I followed your advice and added a compile test checking
Matthieu> PY_VERSION_HEX and #erroring on failure. Here below is the
Matthieu> new patch that I posted here to avoid a new revision.
Thank you. This looks good to me.
Approved-By: Tom Tromey <tom@tromey.com>
Tom
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH v3 3/7] gdb: add new helpers for retrieving a type's fully qualified name
2026-03-09 17:56 [PATCH v3 0/7] gdb: more fixes for Python limited C API support Matthieu Longo
2026-03-09 17:56 ` [PATCH v3 1/7] gdb: introduce rgb_color type to simplify existing code Matthieu Longo
2026-03-09 17:56 ` [PATCH v3 2/7] gdb: fail configure if Python version is too old for limited API Matthieu Longo
@ 2026-03-09 17:56 ` Matthieu Longo
2026-03-24 18:44 ` Tom Tromey
2026-03-09 17:56 ` [PATCH v3 4/7] gdb/python: allow ref_ptr<T, Policy>::new_reference to accept subclasses of T Matthieu Longo
` (4 subsequent siblings)
7 siblings, 1 reply; 25+ messages in thread
From: Matthieu Longo @ 2026-03-09 17:56 UTC (permalink / raw)
To: gdb-patches, Tom Tromey; +Cc: Matthieu Longo
Py_TYPE (self)->tp_name is the traditional idiomatic way to get a Python
type's fully qualified name. However, in the context of the Python
limited API, PyTypeObject is opaque, so the 'tp_name' attribute is no
longer accessible. Additionally, retrieving the type of a Python object
requires Py_TYPE, which is only available as part of the stable API
starting with Python 3.14.
This patch increases minimal Python limited API version from 3.11 to 3.14.
It also introduces two new helpers to retrieve a type's fully qualified
name: gdb_py_tp_name() and gdbpy_py_obj_tp_name(), which extract the fully
qualified name from a PyTypeObject and a PyObject, respectively. Ifdefery
allows these wrappers to select the appropriate API depending on the Python
version and whether the Python limited API is enabled. For any Python
version less than 3.13, gdb_py_tp_name() fallbacks using __qualname__
instead. However, the result may differ slightly in some cases, e.g. the
module name may be missing.
Finally, this patch adapts the existing code to use these wrappers, and
adjusts some test expectations to use the fully qualified name (or
__qualname__ for versions <= 3.13) where it was not previously used.
Note that the corner case where the module name would be missing does not
appear in the testsuite.
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=23830
---
gdb/Makefile.in | 1 +
gdb/configure | 10 +--
gdb/configure.ac | 10 +--
gdb/python/py-arch.c | 3 +-
gdb/python/py-block.c | 3 +-
gdb/python/py-breakpoint.c | 9 ++-
gdb/python/py-connection.c | 2 +-
gdb/python/py-corefile.c | 2 +-
gdb/python/py-disasm.c | 17 ++---
gdb/python/py-frame.c | 4 +-
gdb/python/py-infthread.c | 2 +-
gdb/python/py-mi.c | 2 +-
gdb/python/py-micmd.c | 2 +-
gdb/python/py-obj-type.c | 74 ++++++++++++++++++++++
gdb/python/py-obj-type.h | 29 +++++++++
gdb/python/py-style.c | 14 ++--
gdb/python/py-symbol.c | 3 +-
gdb/python/py-type.c | 3 +-
gdb/python/py-unwind.c | 8 +--
gdb/python/py-utils.c | 4 +-
gdb/python/python-internal.h | 6 +-
gdb/python/python.c | 2 +-
gdb/testsuite/gdb.python/py-disasm.exp.tcl | 8 +--
gdb/testsuite/gdb.python/py-unwind.exp | 2 +-
24 files changed, 167 insertions(+), 53 deletions(-)
create mode 100644 gdb/python/py-obj-type.c
create mode 100644 gdb/python/py-obj-type.h
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 2aa95be968a..4f5966de25c 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -422,6 +422,7 @@ SUBDIR_PYTHON_SRCS = \
python/py-micmd.c \
python/py-newobjfileevent.c \
python/py-objfile.c \
+ python/py-obj-type.c \
python/py-param.c \
python/py-prettyprint.c \
python/py-progspace.c \
diff --git a/gdb/configure b/gdb/configure
index dcd5c030713..3dbf2cc3262 100755
--- a/gdb/configure
+++ b/gdb/configure
@@ -28763,15 +28763,15 @@ fi
if test "$enable_py_limited_api" = yes; then
- # The minimal Python limited API version is currently set to 3.11 for the
- # support of PyBuffer_FillInfo and PyBuffer_Release.
+ # The minimal Python limited API version is currently set to 3.14 for the
+ # support of Py_TYPE().
# The choice of the minimal version for the Python limited API won't be
# frozen until the end of the migration.
prog="import sys
# split strings by '.' and convert to numeric. Append some zeros
# because we need at least 4 digits for the hex conversion.
# map returns an iterator in Python 3.0 and a list in 2.x
-minver = list(map(int, '3.11'.split('.'))) + [0, 0, 0]
+minver = list(map(int, '3.14'.split('.'))) + [0, 0, 0]
minverhex = 0
# xrange is not present in Python 3.0 and range returns an iterator
for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[i]
@@ -28783,12 +28783,12 @@ sys.exit(sys.hexversion < minverhex)"
(exit $ac_status); }; then :
-$as_echo "#define Py_LIMITED_API 0x030b0000" >>confdefs.h
+$as_echo "#define Py_LIMITED_API 0x030e0000" >>confdefs.h
else
- as_fn_error $? "Python limited API support requires at least Python version 3.11" "$LINENO" 5
+ as_fn_error $? "Python limited API support requires at least Python version 3.14" "$LINENO" 5
fi
fi
diff --git a/gdb/configure.ac b/gdb/configure.ac
index 17f648d0b49..b7ae2f3bb3c 100644
--- a/gdb/configure.ac
+++ b/gdb/configure.ac
@@ -1078,15 +1078,15 @@ AC_ARG_ENABLE([py-limited-api],
[enable_py_limited_api=no])
if test "$enable_py_limited_api" = yes; then
- # The minimal Python limited API version is currently set to 3.11 for the
- # support of PyBuffer_FillInfo and PyBuffer_Release.
+ # The minimal Python limited API version is currently set to 3.14 for the
+ # support of Py_TYPE().
# The choice of the minimal version for the Python limited API won't be
# frozen until the end of the migration.
- AM_PYTHON_CHECK_VERSION([$python_prog], [3.11], [
- AC_DEFINE(Py_LIMITED_API, 0x030b0000,
+ AM_PYTHON_CHECK_VERSION([$python_prog], [3.14], [
+ AC_DEFINE(Py_LIMITED_API, 0x030e0000,
[Define if GDB should be built against the Python limited C API.])
],[
- AC_MSG_ERROR([Python limited API support requires at least Python version 3.11])
+ AC_MSG_ERROR([Python limited API support requires at least Python version 3.14])
])
fi
diff --git a/gdb/python/py-arch.c b/gdb/python/py-arch.c
index f40d7da1763..d859d66dc82 100644
--- a/gdb/python/py-arch.c
+++ b/gdb/python/py-arch.c
@@ -338,7 +338,8 @@ archpy_repr (PyObject *self)
auto arch_info = gdbarch_bfd_arch_info (gdbarch);
return PyUnicode_FromFormat ("<%s arch_name=%s printable_name=%s>",
- Py_TYPE (self)->tp_name, arch_info->arch_name,
+ gdbpy_py_obj_tp_name (self),
+ arch_info->arch_name,
arch_info->printable_name);
}
diff --git a/gdb/python/py-block.c b/gdb/python/py-block.c
index 263819e1292..602e86c3766 100644
--- a/gdb/python/py-block.c
+++ b/gdb/python/py-block.c
@@ -485,7 +485,8 @@ blpy_repr (PyObject *self)
if (++written_symbols < len)
str += ", ";
}
- return PyUnicode_FromFormat ("<%s %s {%s}>", Py_TYPE (self)->tp_name,
+ return PyUnicode_FromFormat ("<%s %s {%s}>",
+ gdbpy_py_obj_tp_name (self),
name, str.c_str ());
}
diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
index 408d4b9d857..bdab68ec686 100644
--- a/gdb/python/py-breakpoint.c
+++ b/gdb/python/py-breakpoint.c
@@ -1065,9 +1065,11 @@ bppy_init (PyObject *self, PyObject *args, PyObject *kwargs)
static PyObject *
bppy_repr (PyObject *self)
{
+ const char *tp_name = gdbpy_py_obj_tp_name (self);
+
const auto bp = (struct gdbpy_breakpoint_object*) self;
if (bp->bp == nullptr)
- return PyUnicode_FromFormat ("<%s (invalid)>", Py_TYPE (self)->tp_name);
+ return PyUnicode_FromFormat ("<%s (invalid)>", tp_name);
std::string str = " ";
if (bp->bp->thread != -1)
@@ -1079,7 +1081,7 @@ bppy_repr (PyObject *self)
str.pop_back ();
return PyUnicode_FromFormat ("<%s%s number=%d hits=%d%s>",
- Py_TYPE (self)->tp_name,
+ tp_name,
(bp->bp->enable_state == bp_enabled
? "" : " disabled"), bp->bp->number,
bp->bp->hit_count, str.c_str ());
@@ -1771,7 +1773,8 @@ bplocpy_repr (PyObject *py_self)
str += fn_name;
}
- return PyUnicode_FromFormat ("<%s %s>", Py_TYPE (self)->tp_name,
+ return PyUnicode_FromFormat ("<%s %s>",
+ gdbpy_py_obj_tp_name (py_self),
str.c_str ());
}
diff --git a/gdb/python/py-connection.c b/gdb/python/py-connection.c
index 26cb6cdde57..a8bea4d832f 100644
--- a/gdb/python/py-connection.c
+++ b/gdb/python/py-connection.c
@@ -201,7 +201,7 @@ connpy_repr (PyObject *obj)
return gdb_py_invalid_object_repr (obj);
return PyUnicode_FromFormat ("<%s num=%d, what=\"%s\">",
- Py_TYPE (obj)->tp_name,
+ gdbpy_py_obj_tp_name (obj),
target->connection_number,
make_target_connection_string (target).c_str ());
}
diff --git a/gdb/python/py-corefile.c b/gdb/python/py-corefile.c
index 88fedbd718c..762348121a7 100644
--- a/gdb/python/py-corefile.c
+++ b/gdb/python/py-corefile.c
@@ -362,7 +362,7 @@ cfpy_repr (PyObject *self)
bfd *core_bfd = get_inferior_core_bfd (obj->inferior);
gdb_assert (core_bfd != nullptr);
return PyUnicode_FromFormat ("<%s inferior=%d filename='%s'>",
- Py_TYPE (self)->tp_name,
+ gdbpy_py_obj_tp_name (self),
obj->inferior->num,
bfd_get_filename (core_bfd));
}
diff --git a/gdb/python/py-disasm.c b/gdb/python/py-disasm.c
index 7635e45db56..1c5661dd307 100644
--- a/gdb/python/py-disasm.c
+++ b/gdb/python/py-disasm.c
@@ -304,7 +304,7 @@ disasmpy_info_repr (PyObject *self)
const char *arch_name
= (gdbarch_bfd_arch_info (obj->gdbarch))->printable_name;
return PyUnicode_FromFormat ("<%s address=%s architecture=%s>",
- Py_TYPE (obj)->tp_name,
+ gdbpy_py_obj_tp_name (self),
core_addr_to_string_nz (obj->address),
arch_name);
}
@@ -995,7 +995,7 @@ disasmpy_result_init (PyObject *self, PyObject *args, PyObject *kwargs)
{
PyErr_Format (PyExc_ValueError,
_("Cannot use 'string' and 'parts' when creating %s."),
- Py_TYPE (self)->tp_name);
+ gdbpy_py_obj_tp_name (self));
return -1;
}
@@ -1079,7 +1079,7 @@ disasmpy_result_repr (PyObject *self)
gdb_assert (obj->parts != nullptr);
return PyUnicode_FromFormat ("<%s length=%d string=\"%U\">",
- Py_TYPE (obj)->tp_name,
+ gdbpy_py_obj_tp_name (self),
obj->length,
disasmpy_result_str (self));
}
@@ -1294,7 +1294,7 @@ gdbpy_print_insn (struct gdbarch *gdbarch, CORE_ADDR memaddr,
PyErr_Format
(PyExc_TypeError,
_("Result from Disassembler must be gdb.DisassemblerResult, not %s."),
- Py_TYPE (result.get ())->tp_name);
+ gdbpy_py_obj_tp_name (result.get ()));
gdbpy_print_stack ();
return std::optional<int> (-1);
}
@@ -1381,8 +1381,9 @@ disasmpy_dealloc_result (PyObject *self)
static int
disasmpy_part_init (PyObject *self, PyObject *args, PyObject *kwargs)
{
- PyErr_SetString (PyExc_RuntimeError,
- _("Cannot create instances of DisassemblerPart."));
+ PyErr_Format (PyExc_RuntimeError,
+ _("Cannot create instances of %s."),
+ gdbpy_py_obj_tp_name (self));
return -1;
}
@@ -1419,7 +1420,7 @@ disasmpy_text_part_repr (PyObject *self)
gdb_assert (obj->string != nullptr);
return PyUnicode_FromFormat ("<%s string='%s', style='%s'>",
- Py_TYPE (obj)->tp_name,
+ gdbpy_py_obj_tp_name (self),
obj->string->c_str (),
get_style_name (obj->style));
}
@@ -1462,7 +1463,7 @@ disasmpy_addr_part_repr (PyObject *self)
disasm_addr_part_object *obj = (disasm_addr_part_object *) self;
return PyUnicode_FromFormat ("<%s address='%s'>",
- Py_TYPE (obj)->tp_name,
+ gdbpy_py_obj_tp_name (self),
core_addr_to_string_nz (obj->address));
}
diff --git a/gdb/python/py-frame.c b/gdb/python/py-frame.c
index 23d8eff661f..1420d2ac5b9 100644
--- a/gdb/python/py-frame.c
+++ b/gdb/python/py-frame.c
@@ -95,7 +95,7 @@ frapy_repr (PyObject *self)
const frame_id &fid = frame_obj->frame_id;
return PyUnicode_FromFormat ("<%s level=%d frame-id=%s>",
- Py_TYPE (self)->tp_name,
+ gdbpy_py_obj_tp_name (self),
frame_relative_level (f_info),
fid.to_string ().c_str ());
}
@@ -544,7 +544,7 @@ frapy_read_var (PyObject *self, PyObject *args, PyObject *kw)
{
PyErr_Format (PyExc_TypeError,
_("argument 1 must be gdb.Symbol or str, not %s"),
- Py_TYPE (sym_obj)->tp_name);
+ gdbpy_py_obj_tp_name (sym_obj));
return NULL;
}
diff --git a/gdb/python/py-infthread.c b/gdb/python/py-infthread.c
index 652355990ee..0f29e9fb7a4 100644
--- a/gdb/python/py-infthread.c
+++ b/gdb/python/py-infthread.c
@@ -355,7 +355,7 @@ thpy_repr (PyObject *self)
thread_info *thr = thread_obj->thread;
return PyUnicode_FromFormat ("<%s id=%s target-id=\"%s\">",
- Py_TYPE (self)->tp_name,
+ gdbpy_py_obj_tp_name (self),
print_full_thread_id (thr),
target_pid_to_str (thr->ptid).c_str ());
}
diff --git a/gdb/python/py-mi.c b/gdb/python/py-mi.c
index 5189bda944e..27c79d60636 100644
--- a/gdb/python/py-mi.c
+++ b/gdb/python/py-mi.c
@@ -379,7 +379,7 @@ gdbpy_notify_mi (PyObject *self, PyObject *args, PyObject *kwargs)
PyErr_Format
(PyExc_ValueError,
_("MI notification data must be either None or a dictionary, not %s"),
- Py_TYPE (data)->tp_name);
+ gdbpy_py_obj_tp_name (data));
return nullptr;
}
diff --git a/gdb/python/py-micmd.c b/gdb/python/py-micmd.c
index 0c820751c56..dac8234f095 100644
--- a/gdb/python/py-micmd.c
+++ b/gdb/python/py-micmd.c
@@ -510,7 +510,7 @@ micmdpy_set_installed (PyObject *self, PyObject *newvalue, void *closure)
{
PyErr_Format (PyExc_TypeError,
_("gdb.MICommand.installed must be set to a bool, not %s"),
- newvalue == Py_None ? "None" : Py_TYPE(newvalue)->tp_name);
+ newvalue == Py_None ? "None" : gdbpy_py_obj_tp_name (newvalue));
return -1;
}
diff --git a/gdb/python/py-obj-type.c b/gdb/python/py-obj-type.c
new file mode 100644
index 00000000000..ea0b59a8447
--- /dev/null
+++ b/gdb/python/py-obj-type.c
@@ -0,0 +1,74 @@
+/* Helpers related to Python object type
+
+ 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/>. */
+
+#include "python-internal.h"
+#include "py-obj-type.h"
+
+/* Return the type's fully qualified name from a PyTypeObject. */
+const char *
+gdb_py_tp_name (PyTypeObject *py_type) noexcept
+{
+#if PY_VERSION_HEX >= 0x030d0000
+ /* Note: PyType_GetFullyQualifiedName() was added in version 3.13, and is
+ part of the stable ABI since version 3.13. */
+ PyObject *fully_qualified_name = PyType_GetFullyQualifiedName (py_type);
+ if (fully_qualified_name == nullptr)
+ return nullptr;
+
+ return PyUnicode_AsUTF8AndSize (fully_qualified_name, nullptr);
+
+#else /* PY_VERSION_HEX < 0x030d0000 && ! defined (Py_LIMITED_API) */
+ /* For non-heap types, the fully qualified name corresponds to tp_name. */
+ if (! (PyType_GetFlags (py_type) & Py_TPFLAGS_HEAPTYPE))
+ return py_type->tp_name;
+
+ /* In the absence of PyType_GetFullyQualifiedName(), we fallback using
+ __qualname__ instead. However, the result may differ slightly in some
+ cases, e.g. the module name may be missing. */
+
+# if PY_VERSION_HEX >= 0x030b0000
+ /* Note: PyType_GetQualName() was added in version 3.11. */
+ PyObject *qualname = PyType_GetQualName (py_type);
+ if (qualname == nullptr)
+ return nullptr;
+
+ return PyUnicode_AsUTF8AndSize (qualname, nullptr);
+
+# else
+ /* In the absence of PyType_GetQualName(), fallback on using PyHeapTypeObject
+ which is not part of the public API.
+ Tested on 3.10 which is the oldest supported version at the time of this
+ writing, i.e. February 2026. Hopefully, this workaround should go away
+ when the minimum supported Python version is increased above 3.10. */
+ PyHeapTypeObject *ht = (PyHeapTypeObject *) py_type;
+ if (ht->ht_qualname == nullptr)
+ return nullptr;
+
+ return PyUnicode_AsUTF8AndSize (ht->ht_qualname, nullptr);
+# endif
+#endif
+}
+
+/* Return the type's fully qualified name from a PyObject. */
+const char *
+gdbpy_py_obj_tp_name (PyObject *self) noexcept
+{
+ /* Note: Py_TYPE () is part of the stable ABI since version 3.14. */
+ return gdb_py_tp_name (Py_TYPE (self));
+}
diff --git a/gdb/python/py-obj-type.h b/gdb/python/py-obj-type.h
new file mode 100644
index 00000000000..293647fabfb
--- /dev/null
+++ b/gdb/python/py-obj-type.h
@@ -0,0 +1,29 @@
+/* Helpers related to Python object type
+
+ 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_PY_OBJ_TYPE_H
+#define GDB_PYTHON_PY_OBJ_TYPE_H
+
+/* Return the type's fully qualified name from a PyTypeObject. */
+extern const char *gdb_py_tp_name (PyTypeObject *py_type) noexcept;
+
+/* Return the type's fully qualified name from a PyObject. */
+extern const char *gdbpy_py_obj_tp_name (PyObject *self) noexcept;
+
+#endif /* GDB_PYTHON_PY_OBJ_TYPE_H */
diff --git a/gdb/python/py-style.c b/gdb/python/py-style.c
index aa6eccaadbe..e0b40fe6844 100644
--- a/gdb/python/py-style.c
+++ b/gdb/python/py-style.c
@@ -268,7 +268,7 @@ stylepy_init_from_parts (PyObject *self, PyObject *fg, PyObject *bg,
PyErr_Format
(PyExc_TypeError,
_("'foreground' argument must be gdb.Color or None, not %s."),
- Py_TYPE (fg)->tp_name);
+ gdbpy_py_obj_tp_name (fg));
return -1;
}
@@ -277,7 +277,7 @@ stylepy_init_from_parts (PyObject *self, PyObject *fg, PyObject *bg,
PyErr_Format
(PyExc_TypeError,
_("'background' argument must be gdb.Color or None, not %s."),
- Py_TYPE (bg)->tp_name);
+ gdbpy_py_obj_tp_name (bg));
return -1;
}
@@ -484,7 +484,7 @@ stylepy_set_foreground (PyObject *self, PyObject *newvalue, void *closure)
if (!gdbpy_is_color (newvalue))
{
PyErr_Format (PyExc_TypeError, _("value must be gdb.Color, not %s"),
- Py_TYPE (newvalue)->tp_name);
+ gdbpy_py_obj_tp_name (newvalue));
return -1;
}
@@ -542,7 +542,7 @@ stylepy_set_background (PyObject *self, PyObject *newvalue, void *closure)
if (!gdbpy_is_color (newvalue))
{
PyErr_Format (PyExc_TypeError, _("value must be gdb.Color, not %s"),
- Py_TYPE (newvalue)->tp_name);
+ gdbpy_py_obj_tp_name (newvalue));
return -1;
}
@@ -624,7 +624,7 @@ stylepy_set_intensity (PyObject *self, PyObject *newvalue, void *closure)
PyErr_Format
(PyExc_TypeError,
_("value must be a Long (a gdb.INTENSITY constant), not %s"),
- Py_TYPE (newvalue)->tp_name);
+ gdbpy_py_obj_tp_name (newvalue));
return -1;
}
@@ -734,12 +734,12 @@ stylepy_repr (PyObject *self)
if (style_obj->style_name == nullptr)
return PyUnicode_FromFormat ("<%s fg=%s, bg=%s, intensity=%s>",
- Py_TYPE (self)->tp_name,
+ gdbpy_py_obj_tp_name (self),
fg_str.get (), bg_str.get (),
intensity_str);
else
return PyUnicode_FromFormat ("<%s name='%s', fg=%s, bg=%s, intensity=%s>",
- Py_TYPE (self)->tp_name,
+ gdbpy_py_obj_tp_name (self),
style_obj->style_name, fg_str.get (),
bg_str.get (), intensity_str);
}
diff --git a/gdb/python/py-symbol.c b/gdb/python/py-symbol.c
index fe4d6dac000..e74c8e4b368 100644
--- a/gdb/python/py-symbol.c
+++ b/gdb/python/py-symbol.c
@@ -384,7 +384,8 @@ sympy_repr (PyObject *self)
if (symbol == nullptr)
return gdb_py_invalid_object_repr (self);
- return PyUnicode_FromFormat ("<%s print_name=%s>", Py_TYPE (self)->tp_name,
+ return PyUnicode_FromFormat ("<%s print_name=%s>",
+ gdbpy_py_obj_tp_name (self),
symbol->print_name ());
}
diff --git a/gdb/python/py-type.c b/gdb/python/py-type.c
index 7ba77ad1d4a..ec4126d0589 100644
--- a/gdb/python/py-type.c
+++ b/gdb/python/py-type.c
@@ -1084,7 +1084,8 @@ typy_repr (PyObject *self)
auto py_typename = PyUnicode_Decode (type_name.c_str (), type_name.size (),
host_charset (), NULL);
- return PyUnicode_FromFormat ("<%s code=%s name=%U>", Py_TYPE (self)->tp_name,
+ return PyUnicode_FromFormat ("<%s code=%s name=%U>",
+ gdbpy_py_obj_tp_name (self),
code, py_typename);
}
diff --git a/gdb/python/py-unwind.c b/gdb/python/py-unwind.c
index 9ffa382d093..dcf86f7db3d 100644
--- a/gdb/python/py-unwind.c
+++ b/gdb/python/py-unwind.c
@@ -247,7 +247,7 @@ unwind_infopy_repr (PyObject *self)
if (frame == nullptr)
return PyUnicode_FromFormat ("<%s for an invalid frame>",
- Py_TYPE (self)->tp_name);
+ gdbpy_py_obj_tp_name (self));
std::string saved_reg_names;
struct gdbarch *gdbarch = pending_frame->gdbarch;
@@ -262,7 +262,7 @@ unwind_infopy_repr (PyObject *self)
}
return PyUnicode_FromFormat ("<%s frame #%d, saved_regs=(%s)>",
- Py_TYPE (self)->tp_name,
+ gdbpy_py_obj_tp_name (self),
frame_relative_level (frame),
saved_reg_names.c_str ());
}
@@ -456,7 +456,7 @@ pending_framepy_repr (PyObject *self)
}
return PyUnicode_FromFormat ("<%s level=%d, sp=%s, pc=%s>",
- Py_TYPE (self)->tp_name,
+ gdbpy_py_obj_tp_name (self),
frame_relative_level (frame),
sp_str,
pc_str);
@@ -924,7 +924,7 @@ frame_unwind_python::sniff (const frame_info_ptr &this_frame,
gdb_assert (pyo_unwind_info != nullptr);
if (!PyObject_TypeCheck (pyo_unwind_info, &unwind_info_object_type))
error (_("an Unwinder should return gdb.UnwindInfo, not %s."),
- Py_TYPE (pyo_unwind_info)->tp_name);
+ gdbpy_py_obj_tp_name (pyo_unwind_info));
{
unwind_info_object *unwind_info =
diff --git a/gdb/python/py-utils.c b/gdb/python/py-utils.c
index 484fc4611b7..96f1cb1ee31 100644
--- a/gdb/python/py-utils.c
+++ b/gdb/python/py-utils.c
@@ -362,7 +362,7 @@ gdb_py_generic_getattro (PyObject *self, PyObject *attr)
Therefore, we must explicitly raise an AttributeError in this case. */
PyErr_Format (PyExc_AttributeError,
"'%s' object has no attribute '%s'",
- Py_TYPE (self)->tp_name,
+ gdbpy_py_obj_tp_name (self),
PyUnicode_AsUTF8AndSize (attr, nullptr));
return nullptr;
}
@@ -700,5 +700,5 @@ gdbpy_fix_doc_string_indentation (gdb::unique_xmalloc_ptr<char> doc)
PyObject *
gdb_py_invalid_object_repr (PyObject *self)
{
- return PyUnicode_FromFormat ("<%s (invalid)>", Py_TYPE (self)->tp_name);
+ return PyUnicode_FromFormat ("<%s (invalid)>", gdbpy_py_obj_tp_name (self));
}
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index bdc960ed208..447da3f96e4 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -59,6 +59,7 @@
#include <Python.h>
#include <frameobject.h>
#include "py-ref.h"
+#include "py-obj-type.h"
static_assert (PY_VERSION_HEX >= 0x03040000);
@@ -1124,12 +1125,13 @@ gdbpy_type_ready (PyTypeObject *type, PyObject *mod = nullptr)
{
if (PyType_Ready (type) < 0)
return -1;
+ const char *tp_name = gdb_py_tp_name (type);
if (mod == nullptr)
{
- gdb_assert (startswith (type->tp_name, "gdb."));
+ gdb_assert (startswith (tp_name, "gdb."));
mod = gdb_module;
}
- const char *dot = strrchr (type->tp_name, '.');
+ const char *dot = strrchr (tp_name, '.');
gdb_assert (dot != nullptr);
return gdb_pymodule_addobject (mod, dot + 1, (PyObject *) type);
}
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 5474b8d644f..27cb3417c89 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1579,7 +1579,7 @@ gdbpy_write (PyObject *self, PyObject *args, PyObject *kw)
PyErr_Format
(PyExc_TypeError,
_("'style' argument must be gdb.Style or None, not %s."),
- Py_TYPE (style_obj)->tp_name);
+ gdbpy_py_obj_tp_name (style_obj));
return nullptr;
}
diff --git a/gdb/testsuite/gdb.python/py-disasm.exp.tcl b/gdb/testsuite/gdb.python/py-disasm.exp.tcl
index 07131a44bff..e4391fa59ce 100644
--- a/gdb/testsuite/gdb.python/py-disasm.exp.tcl
+++ b/gdb/testsuite/gdb.python/py-disasm.exp.tcl
@@ -124,7 +124,7 @@ set test_plans \
[list "" "${base_pattern}\r\n.*"] \
[list "GlobalNullDisassembler" "${base_pattern}\r\n.*"] \
[list "ShowInfoRepr" "${base_pattern}\\s+## <gdb.disassembler.DisassembleInfo address=$hex architecture=\[^>\]+>\r\n.*"] \
- [list "ShowInfoSubClassRepr" "${base_pattern}\\s+## <MyInfo address=$hex architecture=\[^>\]+>\r\n.*"] \
+ [list "ShowInfoSubClassRepr" "${base_pattern}\\s+## <ShowInfoSubClassRepr.MyInfo address=$hex architecture=\[^>\]+>\r\n.*"] \
[list "ShowResultRepr" "${base_pattern}\\s+## <gdb.disassembler.DisassemblerResult length=$decimal string=\"\[^\r\n\]+\">\r\n.*"] \
[list "ShowResultStr" "${base_pattern}\\s+## ${nop}\r\n.*"] \
[list "GlobalPreInfoDisassembler" "${base_pattern}\\s+## ad = $hex, ar = ${curr_arch}\r\n.*"] \
@@ -154,10 +154,10 @@ set test_plans \
"Buffer returned from read_memory is sized $decimal instead of the expected $decimal"]] \
[list "ResultOfWrongType" \
[make_exception_pattern "TypeError" \
- "Result from Disassembler must be gdb.DisassemblerResult, not Blah."]] \
+ "Result from Disassembler must be gdb.DisassemblerResult, not ResultOfWrongType.Blah."]] \
[list "ResultOfVeryWrongType" \
[make_exception_pattern "TypeError" \
- "Result from Disassembler must be gdb.DisassemblerResult, not Blah."]] \
+ "Result from Disassembler must be gdb.DisassemblerResult, not ResultOfVeryWrongType.Blah."]] \
[list "ErrorCreatingTextPart_NoArgs" \
[make_exception_pattern "TypeError" \
[missing_arg_pattern "style" 1]]] \
@@ -337,7 +337,7 @@ foreach len {0 -1} {
foreach type {DisassemblerTextPart DisassemblerAddressPart} {
gdb_test "python result = gdb.disassembler.${type}()" \
[multi_line \
- "RuntimeError.*: Cannot create instances of DisassemblerPart\\." \
+ "RuntimeError.*: Cannot create instances of gdb.disassembler.${type}\\." \
"Error occurred in Python.*"] \
"try to create an instance of ${type}"
}
diff --git a/gdb/testsuite/gdb.python/py-unwind.exp b/gdb/testsuite/gdb.python/py-unwind.exp
index 8c90da50a1d..784d9dfcb64 100644
--- a/gdb/testsuite/gdb.python/py-unwind.exp
+++ b/gdb/testsuite/gdb.python/py-unwind.exp
@@ -249,7 +249,7 @@ with_test_prefix "bad object unwinder" {
gdb_test_no_output "python obj = bad_object_unwinder(\"bad-object\")"
gdb_test_no_output "python gdb.unwinder.register_unwinder(None, obj, replace=True)"
gdb_test "backtrace" \
- "Python Exception <class 'gdb.error'>: an Unwinder should return gdb.UnwindInfo, not Blah\\.\r\n.*"
+ "Python Exception <class 'gdb.error'>: an Unwinder should return gdb.UnwindInfo, not bad_object_unwinder.+Blah\\.\r\n.*"
}
# Gather information about every frame.
--
2.53.0
^ permalink raw reply [flat|nested] 25+ messages in thread* Re: [PATCH v3 3/7] gdb: add new helpers for retrieving a type's fully qualified name
2026-03-09 17:56 ` [PATCH v3 3/7] gdb: add new helpers for retrieving a type's fully qualified name Matthieu Longo
@ 2026-03-24 18:44 ` Tom Tromey
0 siblings, 0 replies; 25+ messages in thread
From: Tom Tromey @ 2026-03-24 18:44 UTC (permalink / raw)
To: Matthieu Longo; +Cc: gdb-patches, Tom Tromey
>>>>> Matthieu Longo <matthieu.longo@arm.com> writes:
> Py_TYPE (self)->tp_name is the traditional idiomatic way to get a Python
> type's fully qualified name. However, in the context of the Python
> limited API, PyTypeObject is opaque, so the 'tp_name' attribute is no
> longer accessible. Additionally, retrieving the type of a Python object
> requires Py_TYPE, which is only available as part of the stable API
> starting with Python 3.14.
> This patch increases minimal Python limited API version from 3.11 to 3.14.
> It also introduces two new helpers to retrieve a type's fully qualified
> name: gdb_py_tp_name() and gdbpy_py_obj_tp_name(), which extract the fully
> qualified name from a PyTypeObject and a PyObject, respectively. Ifdefery
> allows these wrappers to select the appropriate API depending on the Python
> version and whether the Python limited API is enabled. For any Python
> version less than 3.13, gdb_py_tp_name() fallbacks using __qualname__
> instead. However, the result may differ slightly in some cases, e.g. the
> module name may be missing.
> Finally, this patch adapts the existing code to use these wrappers, and
> adjusts some test expectations to use the fully qualified name (or
> __qualname__ for versions <= 3.13) where it was not previously used.
> Note that the corner case where the module name would be missing does not
> appear in the testsuite.
Thanks, this looks good to me.
Approved-By: Tom Tromey <tom@tromey.com>
Tom
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH v3 4/7] gdb/python: allow ref_ptr<T, Policy>::new_reference to accept subclasses of T
2026-03-09 17:56 [PATCH v3 0/7] gdb: more fixes for Python limited C API support Matthieu Longo
` (2 preceding siblings ...)
2026-03-09 17:56 ` [PATCH v3 3/7] gdb: add new helpers for retrieving a type's fully qualified name Matthieu Longo
@ 2026-03-09 17:56 ` Matthieu Longo
2026-03-09 19:40 ` Tom Tromey
2026-03-09 17:56 ` [PATCH v3 5/7] gdb/python: flatten functions calling PyObject_New and use gdbpy_ref Matthieu Longo
` (3 subsequent siblings)
7 siblings, 1 reply; 25+ messages in thread
From: Matthieu Longo @ 2026-03-09 17:56 UTC (permalink / raw)
To: gdb-patches, Tom Tromey; +Cc: Matthieu Longo
When ref_ptr<T,Policy>::new_reference() is specialized for 'PyObject'
(i.e. gdbpy_ref<>), it currently requires the argument type to be exactly
'PyObject *'. As a result, pointers to subclasses of 'PyObject' must be
explicitly cast before being passed, making call sites unnecessarily
verbose.
This patch makes ref_ptr<T,Policy>::new_reference() a template method
that accepts both T and subclasses of T, performing the cast to 'T *'
internally when needed. This removes redundant casts at call sites
without changing behavior.
---
gdb/python/py-block.c | 2 +-
gdb/python/py-inferior.c | 2 +-
gdbsupport/gdb_ref_ptr.h | 5 ++++-
3 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/gdb/python/py-block.c b/gdb/python/py-block.c
index 602e86c3766..d56253141f0 100644
--- a/gdb/python/py-block.c
+++ b/gdb/python/py-block.c
@@ -346,7 +346,7 @@ block_to_block_object (const struct block *block, struct objfile *objfile)
block_object *result = (block_object *) htab_find_with_hash (table, block,
hash);
if (result != nullptr)
- return gdbpy_ref<>::new_reference ((PyObject *) result);
+ return gdbpy_ref<>::new_reference (result);
result = PyObject_New (block_object, &block_object_type);
if (result == nullptr)
diff --git a/gdb/python/py-inferior.c b/gdb/python/py-inferior.c
index 76e3da9f620..e55360282b6 100644
--- a/gdb/python/py-inferior.c
+++ b/gdb/python/py-inferior.c
@@ -400,7 +400,7 @@ infpy_threads (PyObject *self, PyObject *args)
for (const thread_map_t::value_type &entry : *inf_obj->threads)
{
- auto thr = gdbpy_ref<>::new_reference ((PyObject *) entry.second.get ());
+ auto thr = gdbpy_ref<>::new_reference (entry.second.get ());
if (PyTuple_SetItem (tuple.get (), i++, thr.release ()) < 0)
return nullptr;
}
diff --git a/gdbsupport/gdb_ref_ptr.h b/gdbsupport/gdb_ref_ptr.h
index 4352ab3e7c0..0eb654324c6 100644
--- a/gdbsupport/gdb_ref_ptr.h
+++ b/gdbsupport/gdb_ref_ptr.h
@@ -197,9 +197,12 @@ class ref_ptr
}
/* Acquire a new reference and return a ref_ptr that owns it. */
- static ref_ptr<T, Policy> new_reference (T *obj)
+ template <class TObj>
+ static ref_ptr<T, Policy> new_reference (TObj *obj)
{
Policy::incref (obj);
+ if constexpr (std::is_base_of<T, TObj>::value)
+ return ref_ptr<T, Policy> (static_cast<T *> (obj));
return ref_ptr<T, Policy> (obj);
}
--
2.53.0
^ permalink raw reply [flat|nested] 25+ messages in thread* Re: [PATCH v3 4/7] gdb/python: allow ref_ptr<T, Policy>::new_reference to accept subclasses of T
2026-03-09 17:56 ` [PATCH v3 4/7] gdb/python: allow ref_ptr<T, Policy>::new_reference to accept subclasses of T Matthieu Longo
@ 2026-03-09 19:40 ` Tom Tromey
2026-03-10 12:26 ` Matthieu Longo
0 siblings, 1 reply; 25+ messages in thread
From: Tom Tromey @ 2026-03-09 19:40 UTC (permalink / raw)
To: Matthieu Longo; +Cc: gdb-patches, Tom Tromey
>>>>> Matthieu Longo <matthieu.longo@arm.com> writes:
> When ref_ptr<T,Policy>::new_reference() is specialized for 'PyObject'
> (i.e. gdbpy_ref<>), it currently requires the argument type to be exactly
> 'PyObject *'. As a result, pointers to subclasses of 'PyObject' must be
> explicitly cast before being passed, making call sites unnecessarily
> verbose.
> This patch makes ref_ptr<T,Policy>::new_reference() a template method
> that accepts both T and subclasses of T, performing the cast to 'T *'
> internally when needed. This removes redundant casts at call sites
> without changing behavior.
This seems fine but I noticed something weird.
Approved-By: Tom Tromey <tom@tromey.com>
> block_object *result = (block_object *) htab_find_with_hash (table, block,
> hash);
> if (result != nullptr)
> - return gdbpy_ref<>::new_reference ((PyObject *) result);
> + return gdbpy_ref<>::new_reference (result);
... this part makes sense to me.
> for (const thread_map_t::value_type &entry : *inf_obj->threads)
> {
> - auto thr = gdbpy_ref<>::new_reference ((PyObject *) entry.second.get ());
> + auto thr = gdbpy_ref<>::new_reference (entry.second.get ());
I guess it isn't super important but new_reference isn't really needed
for a gdbpy_ref<> since we already have a copy constructor. So just
gdbpy_ref<> thr = entry.second;
... should do it?
Perhaps an explicit new_reference is more clear.
Tom
^ permalink raw reply [flat|nested] 25+ messages in thread* Re: [PATCH v3 4/7] gdb/python: allow ref_ptr<T, Policy>::new_reference to accept subclasses of T
2026-03-09 19:40 ` Tom Tromey
@ 2026-03-10 12:26 ` Matthieu Longo
0 siblings, 0 replies; 25+ messages in thread
From: Matthieu Longo @ 2026-03-10 12:26 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
On 09/03/2026 19:40, Tom Tromey wrote:
>>>>>> Matthieu Longo <matthieu.longo@arm.com> writes:
>
>> When ref_ptr<T,Policy>::new_reference() is specialized for 'PyObject'
>> (i.e. gdbpy_ref<>), it currently requires the argument type to be exactly
>> 'PyObject *'. As a result, pointers to subclasses of 'PyObject' must be
>> explicitly cast before being passed, making call sites unnecessarily
>> verbose.
>
>> This patch makes ref_ptr<T,Policy>::new_reference() a template method
>> that accepts both T and subclasses of T, performing the cast to 'T *'
>> internally when needed. This removes redundant casts at call sites
>> without changing behavior.
>
> This seems fine but I noticed something weird.
>
> Approved-By: Tom Tromey <tom@tromey.com>
>
>> block_object *result = (block_object *) htab_find_with_hash (table, block,
>> hash);
>> if (result != nullptr)
>> - return gdbpy_ref<>::new_reference ((PyObject *) result);
>> + return gdbpy_ref<>::new_reference (result);
>
> ... this part makes sense to me.
>
>> for (const thread_map_t::value_type &entry : *inf_obj->threads)
>> {
>> - auto thr = gdbpy_ref<>::new_reference ((PyObject *) entry.second.get ());
>> + auto thr = gdbpy_ref<>::new_reference (entry.second.get ());
>
> I guess it isn't super important but new_reference isn't really needed
> for a gdbpy_ref<> since we already have a copy constructor. So just
>
> gdbpy_ref<> thr = entry.second;
>
> ... should do it?
>
I hadn't paid attention that entry.second was a gdbpy_ref<>.
Changed to what you suggested.
Matthieu
> Perhaps an explicit new_reference is more clear.
>
> Tom
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH v3 5/7] gdb/python: flatten functions calling PyObject_New and use gdbpy_ref
2026-03-09 17:56 [PATCH v3 0/7] gdb: more fixes for Python limited C API support Matthieu Longo
` (3 preceding siblings ...)
2026-03-09 17:56 ` [PATCH v3 4/7] gdb/python: allow ref_ptr<T, Policy>::new_reference to accept subclasses of T Matthieu Longo
@ 2026-03-09 17:56 ` Matthieu Longo
2026-03-09 20:07 ` Tom Tromey
2026-03-09 17:56 ` [PATCH v3 6/7] gdb/python: add gdbpy_dict_wrapper:allocate_dict helper Matthieu Longo
` (2 subsequent siblings)
7 siblings, 1 reply; 25+ messages in thread
From: Matthieu Longo @ 2026-03-09 17:56 UTC (permalink / raw)
To: gdb-patches, Tom Tromey; +Cc: Matthieu Longo
This patch aims at systematically using gdbpy_ref<> at all call sites
of PyObject_New(). This prepares for future patches that expect
gdbby_ref<> parameters and affect return handling.
As part of this change, flattening the affected functions so that the
return logic becomes clearer and more flexible to adjust.
---
gdb/python/py-corefile.c | 48 +++++++++++++++++++--------------------
gdb/python/py-inferior.c | 35 +++++++++++++---------------
gdb/python/py-progspace.c | 31 +++++++++++++------------
3 files changed, 57 insertions(+), 57 deletions(-)
diff --git a/gdb/python/py-corefile.c b/gdb/python/py-corefile.c
index 762348121a7..de88a964efb 100644
--- a/gdb/python/py-corefile.c
+++ b/gdb/python/py-corefile.c
@@ -119,33 +119,33 @@ gdbpy_core_file_from_inferior (inferior *inf)
return gdbpy_ref<>::new_reference (Py_None);
PyObject *result = (PyObject *) cfpy_inferior_corefile_data_key.get (inf);
- if (result == nullptr)
- {
- gdbpy_ref<corefile_object> object
- (PyObject_New (corefile_object, &corefile_object_type));
- if (object == nullptr)
- return nullptr;
+ if (result != nullptr)
+ return gdbpy_ref<>::new_reference (result);
- /* Ensure the 'inferior' field is set to NULL. If the PyDict_New
- call fails then the gdb.Corefile will be discarded and
- cfpy_dealloc will be called, which requires that the 'inferior' be
- set to NULL. */
- object->inferior = nullptr;
- object->mapped_files = nullptr;
- object->dict = PyDict_New ();
- if (object->dict == nullptr)
- return nullptr;
+ gdbpy_ref<corefile_object> object
+ (PyObject_New (corefile_object, &corefile_object_type));
+ if (object == nullptr)
+ return nullptr;
- /* Now that the gdb.Corefile has been successfully initialised and we
- know that it is going to be passed back to the user, move it out
- of the invalid state by setting the 'inferior' field to a non NULL
- value. */
- object->inferior = inf;
- cfpy_inferior_corefile_data_key.set (inf, object.get ());
- result = (PyObject *) object.release ();
- }
+ /* Ensure the 'inferior' field is set to NULL. If the PyDict_New call fails
+ then the gdb.Corefile will be discarded and cfpy_dealloc will be called,
+ which requires that the 'inferior' be set to NULL. */
+ object->inferior = nullptr;
+ object->mapped_files = nullptr;
+ object->dict = PyDict_New ();
+ if (object->dict == nullptr)
+ return nullptr;
+
+ /* Now that the gdb.Corefile has been successfully initialised and we know
+ that it is going to be passed back to the user, move it out of the invalid
+ state by setting the 'inferior' field to a non NULL value. */
+ object->inferior = inf;
+
+ /* PyObject_New initializes the new object with a refcount of 1. This counts
+ for the reference we are keeping in the inferior corefile data. */
+ cfpy_inferior_corefile_data_key.set (inf, object.get ());
- return gdbpy_ref<>::new_reference (result);
+ return gdbpy_ref<>::new_reference (object.release ());
}
/* Return true if OBJ is valid. */
diff --git a/gdb/python/py-inferior.c b/gdb/python/py-inferior.c
index e55360282b6..4ad0ea1b264 100644
--- a/gdb/python/py-inferior.c
+++ b/gdb/python/py-inferior.c
@@ -213,29 +213,26 @@ python_free_objfile (struct objfile *objfile)
gdbpy_ref<inferior_object>
inferior_to_inferior_object (struct inferior *inferior)
{
- inferior_object *inf_obj;
+ inferior_object *result = infpy_inf_data_key.get (inferior);
+ if (result != nullptr)
+ return gdbpy_ref<inferior_object>::new_reference (result);
- inf_obj = infpy_inf_data_key.get (inferior);
- if (!inf_obj)
- {
- inf_obj = PyObject_New (inferior_object, &inferior_object_type);
- if (!inf_obj)
- return NULL;
+ gdbpy_ref<inferior_object> inf_obj
+ (PyObject_New (inferior_object, &inferior_object_type));
+ if (inf_obj == nullptr)
+ return nullptr;
- inf_obj->inferior = inferior;
- inf_obj->threads = new thread_map_t ();
- inf_obj->dict = PyDict_New ();
- if (inf_obj->dict == nullptr)
- return nullptr;
+ inf_obj->inferior = inferior;
+ inf_obj->threads = new thread_map_t ();
+ inf_obj->dict = PyDict_New ();
+ if (inf_obj->dict == nullptr)
+ return nullptr;
- /* PyObject_New initializes the new object with a refcount of 1. This
- counts for the reference we are keeping in the inferior data. */
- infpy_inf_data_key.set (inferior, inf_obj);
- }
+ /* PyObject_New initializes the new object with a refcount of 1. This counts
+ for the reference we are keeping in the inferior data. */
+ infpy_inf_data_key.set (inferior, inf_obj.get ());
- /* We are returning a new reference. */
- gdb_assert (inf_obj != nullptr);
- return gdbpy_ref<inferior_object>::new_reference (inf_obj);
+ return gdbpy_ref<inferior_object>::new_reference (inf_obj.release ());
}
/* Called when a new inferior is created. Notifies any Python event
diff --git a/gdb/python/py-progspace.c b/gdb/python/py-progspace.c
index 5a23c4c7177..f2585103346 100644
--- a/gdb/python/py-progspace.c
+++ b/gdb/python/py-progspace.c
@@ -585,21 +585,24 @@ gdbpy_ref<>
pspace_to_pspace_object (struct program_space *pspace)
{
PyObject *result = (PyObject *) pspy_pspace_data_key.get (pspace);
- if (result == NULL)
- {
- gdbpy_ref<pspace_object> object
- ((pspace_object *) PyObject_New (pspace_object, &pspace_object_type));
- if (object == NULL)
- return NULL;
- if (!pspy_initialize (object))
- return NULL;
-
- object->pspace = pspace;
- pspy_pspace_data_key.set (pspace, object.get ());
- result = (PyObject *) object.release ();
- }
+ if (result != nullptr)
+ return gdbpy_ref<>::new_reference (result);
+
+ gdbpy_ref<pspace_object> object
+ (PyObject_New (pspace_object, &pspace_object_type));
+ if (object == nullptr)
+ return nullptr;
+
+ if (!pspy_initialize (object))
+ return nullptr;
+
+ object->pspace = pspace;
+
+ /* PyObject_New initializes the new object with a refcount of 1. This counts
+ for the reference we are keeping in the pspace data. */
+ pspy_pspace_data_key.set (pspace, object.get ());
- return gdbpy_ref<>::new_reference (result);
+ return gdbpy_ref<>::new_reference (object.release ());
}
/* See python-internal.h. */
--
2.53.0
^ permalink raw reply [flat|nested] 25+ messages in thread* Re: [PATCH v3 5/7] gdb/python: flatten functions calling PyObject_New and use gdbpy_ref
2026-03-09 17:56 ` [PATCH v3 5/7] gdb/python: flatten functions calling PyObject_New and use gdbpy_ref Matthieu Longo
@ 2026-03-09 20:07 ` Tom Tromey
0 siblings, 0 replies; 25+ messages in thread
From: Tom Tromey @ 2026-03-09 20:07 UTC (permalink / raw)
To: Matthieu Longo; +Cc: gdb-patches, Tom Tromey
>>>>> Matthieu Longo <matthieu.longo@arm.com> writes:
> This patch aims at systematically using gdbpy_ref<> at all call sites
> of PyObject_New(). This prepares for future patches that expect
> gdbby_ref<> parameters and affect return handling.
> As part of this change, flattening the affected functions so that the
> return logic becomes clearer and more flexible to adjust.
Thanks.
Approved-By: Tom Tromey <tom@tromey.com>
> + /* PyObject_New initializes the new object with a refcount of 1. This counts
> + for the reference we are keeping in the inferior corefile data. */
> + cfpy_inferior_corefile_data_key.set (inf, object.get ());
> - return gdbpy_ref<>::new_reference (result);
> + return gdbpy_ref<>::new_reference (object.release ());
Ok, now I see what you meant here.
It's a bit confusing without context since the .release really "should"
appear at the original assignment. But this is equivalent.
Tom
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH v3 6/7] gdb/python: add gdbpy_dict_wrapper:allocate_dict helper
2026-03-09 17:56 [PATCH v3 0/7] gdb: more fixes for Python limited C API support Matthieu Longo
` (4 preceding siblings ...)
2026-03-09 17:56 ` [PATCH v3 5/7] gdb/python: flatten functions calling PyObject_New and use gdbpy_ref Matthieu Longo
@ 2026-03-09 17:56 ` Matthieu Longo
2026-03-09 20:09 ` Tom Tromey
2026-03-09 17:56 ` [PATCH v3 7/7] gdb/python: add accessor helpers for __dict__ in Python extension objects Matthieu Longo
2026-03-23 10:28 ` [PATCH v3 0/7] gdb: more fixes for Python limited C API support Matthieu Longo
7 siblings, 1 reply; 25+ messages in thread
From: Matthieu Longo @ 2026-03-09 17:56 UTC (permalink / raw)
To: gdb-patches, Tom Tromey; +Cc: Matthieu Longo
Python extension objects that support __dict__ must inherit from
gdbpy_dict_wrapper, a wrapper class that stores the PyObject
corresponding to the __dict__ attribute.
Currently, management of this dictionary is not centralized, and
each Python extension object implements its own logic to create,
access, and destroy it.
This patch focuses on the allocation of the dictionary, introduces
a new method, gdbpy_dict_wrapper::allocate_dict(), and
adapts the existing code to use this method.
---
gdb/python/py-corefile.c | 3 +--
gdb/python/py-event.c | 7 +++----
gdb/python/py-inferior.c | 3 +--
gdb/python/py-infthread.c | 7 +++----
gdb/python/py-objfile.c | 7 +++----
gdb/python/py-progspace.c | 3 +--
gdb/python/py-ref.h | 7 +++++++
gdb/python/py-type.c | 9 +++------
8 files changed, 22 insertions(+), 24 deletions(-)
diff --git a/gdb/python/py-corefile.c b/gdb/python/py-corefile.c
index de88a964efb..0c2821cb07e 100644
--- a/gdb/python/py-corefile.c
+++ b/gdb/python/py-corefile.c
@@ -132,8 +132,7 @@ gdbpy_core_file_from_inferior (inferior *inf)
which requires that the 'inferior' be set to NULL. */
object->inferior = nullptr;
object->mapped_files = nullptr;
- object->dict = PyDict_New ();
- if (object->dict == nullptr)
+ if (!object->allocate_dict ())
return nullptr;
/* Now that the gdb.Corefile has been successfully initialised and we know
diff --git a/gdb/python/py-event.c b/gdb/python/py-event.c
index 39e1dd9c75f..8192d1cefa0 100644
--- a/gdb/python/py-event.c
+++ b/gdb/python/py-event.c
@@ -31,11 +31,10 @@ create_event_object (PyTypeObject *py_type)
{
gdbpy_ref<event_object> event_obj (PyObject_New (event_object, py_type));
if (event_obj == NULL)
- return NULL;
+ return nullptr;
- event_obj->dict = PyDict_New ();
- if (!event_obj->dict)
- return NULL;
+ if (!event_obj->allocate_dict ())
+ return nullptr;
return event_obj;
}
diff --git a/gdb/python/py-inferior.c b/gdb/python/py-inferior.c
index 4ad0ea1b264..eaf224fe808 100644
--- a/gdb/python/py-inferior.c
+++ b/gdb/python/py-inferior.c
@@ -224,8 +224,7 @@ inferior_to_inferior_object (struct inferior *inferior)
inf_obj->inferior = inferior;
inf_obj->threads = new thread_map_t ();
- inf_obj->dict = PyDict_New ();
- if (inf_obj->dict == nullptr)
+ if (!inf_obj->allocate_dict ())
return nullptr;
/* PyObject_New initializes the new object with a refcount of 1. This counts
diff --git a/gdb/python/py-infthread.c b/gdb/python/py-infthread.c
index 0f29e9fb7a4..7afec0112fb 100644
--- a/gdb/python/py-infthread.c
+++ b/gdb/python/py-infthread.c
@@ -41,16 +41,15 @@ create_thread_object (struct thread_info *tp)
gdbpy_ref<inferior_object> inf_obj = inferior_to_inferior_object (tp->inf);
if (inf_obj == NULL)
- return NULL;
+ return nullptr;
thread_obj.reset (PyObject_New (thread_object, &thread_object_type));
if (thread_obj == NULL)
- return NULL;
+ return nullptr;
thread_obj->thread = tp;
thread_obj->inf_obj = (PyObject *) inf_obj.release ();
- thread_obj->dict = PyDict_New ();
- if (thread_obj->dict == nullptr)
+ if (!thread_obj->allocate_dict ())
return nullptr;
return thread_obj;
diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c
index bbe21d32549..daf1b44747c 100644
--- a/gdb/python/py-objfile.c
+++ b/gdb/python/py-objfile.c
@@ -201,12 +201,11 @@ objfpy_dealloc (PyObject *o)
static bool
objfpy_initialize (gdbpy_ref<objfile_object> &self)
{
- self->objfile = NULL;
-
- self->dict = PyDict_New ();
- if (self->dict == NULL)
+ if (!self->allocate_dict ())
return false;
+ self->objfile = NULL;
+
self->printers = PyList_New (0);
if (self->printers == NULL)
return false;
diff --git a/gdb/python/py-progspace.c b/gdb/python/py-progspace.c
index f2585103346..d1a8479588f 100644
--- a/gdb/python/py-progspace.c
+++ b/gdb/python/py-progspace.c
@@ -171,8 +171,7 @@ pspy_initialize (gdbpy_ref<pspace_object> &self)
{
self->pspace = NULL;
- self->dict = PyDict_New ();
- if (self->dict == NULL)
+ if (!self->allocate_dict ())
return false;
self->printers = PyList_New (0);
diff --git a/gdb/python/py-ref.h b/gdb/python/py-ref.h
index 0a56436634d..f0cdb38498e 100644
--- a/gdb/python/py-ref.h
+++ b/gdb/python/py-ref.h
@@ -77,6 +77,13 @@ struct gdbpy_dict_wrapper : public PyObject
auto *wrapper = reinterpret_cast<gdbpy_dict_wrapper *> (self);
return &wrapper->dict;
}
+
+ bool
+ allocate_dict ()
+ {
+ dict = PyDict_New ();
+ return dict != nullptr;
+ }
};
#endif /* GDB_PYTHON_PY_REF_H */
diff --git a/gdb/python/py-type.c b/gdb/python/py-type.c
index ec4126d0589..0893da5aec9 100644
--- a/gdb/python/py-type.c
+++ b/gdb/python/py-type.c
@@ -93,12 +93,9 @@ field_new (void)
gdbpy_ref<field_object> result (PyObject_New (field_object,
&field_object_type));
- if (result != NULL)
- {
- result->dict = PyDict_New ();
- if (!result->dict)
- return NULL;
- }
+ if (result != nullptr && !result->allocate_dict ())
+ return nullptr;
+
return (PyObject *) result.release ();
}
--
2.53.0
^ permalink raw reply [flat|nested] 25+ messages in thread* Re: [PATCH v3 6/7] gdb/python: add gdbpy_dict_wrapper:allocate_dict helper
2026-03-09 17:56 ` [PATCH v3 6/7] gdb/python: add gdbpy_dict_wrapper:allocate_dict helper Matthieu Longo
@ 2026-03-09 20:09 ` Tom Tromey
2026-03-10 12:26 ` Matthieu Longo
0 siblings, 1 reply; 25+ messages in thread
From: Tom Tromey @ 2026-03-09 20:09 UTC (permalink / raw)
To: Matthieu Longo; +Cc: gdb-patches, Tom Tromey
>>>>> Matthieu Longo <matthieu.longo@arm.com> writes:
> Python extension objects that support __dict__ must inherit from
> gdbpy_dict_wrapper, a wrapper class that stores the PyObject
> corresponding to the __dict__ attribute.
> Currently, management of this dictionary is not centralized, and
> each Python extension object implements its own logic to create,
> access, and destroy it.
> This patch focuses on the allocation of the dictionary, introduces
> a new method, gdbpy_dict_wrapper::allocate_dict(), and
> adapts the existing code to use this method.
A nit.
> +
> + bool
> + allocate_dict ()
> + {
This should have an intro comment.
And, the type and name should appear on the same line, that's the style
for methods.
Ok with that change.
Approved-By: Tom Tromey <tom@tromey.com>
Tom
^ permalink raw reply [flat|nested] 25+ messages in thread* Re: [PATCH v3 6/7] gdb/python: add gdbpy_dict_wrapper:allocate_dict helper
2026-03-09 20:09 ` Tom Tromey
@ 2026-03-10 12:26 ` Matthieu Longo
0 siblings, 0 replies; 25+ messages in thread
From: Matthieu Longo @ 2026-03-10 12:26 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
On 09/03/2026 20:09, Tom Tromey wrote:
>>>>>> Matthieu Longo <matthieu.longo@arm.com> writes:
>
>> Python extension objects that support __dict__ must inherit from
>> gdbpy_dict_wrapper, a wrapper class that stores the PyObject
>> corresponding to the __dict__ attribute.
>
>> Currently, management of this dictionary is not centralized, and
>> each Python extension object implements its own logic to create,
>> access, and destroy it.
>
>> This patch focuses on the allocation of the dictionary, introduces
>> a new method, gdbpy_dict_wrapper::allocate_dict(), and
>> adapts the existing code to use this method.
>
> A nit.
>
>> +
>> + bool
>> + allocate_dict ()
>> + {
>
> This should have an intro comment.
> And, the type and name should appear on the same line, that's the style
> for methods.
>
> Ok with that change.
> Approved-By: Tom Tromey <tom@tromey.com>
>
> Tom
Fixed.
Matthieu
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH v3 7/7] gdb/python: add accessor helpers for __dict__ in Python extension objects
2026-03-09 17:56 [PATCH v3 0/7] gdb: more fixes for Python limited C API support Matthieu Longo
` (5 preceding siblings ...)
2026-03-09 17:56 ` [PATCH v3 6/7] gdb/python: add gdbpy_dict_wrapper:allocate_dict helper Matthieu Longo
@ 2026-03-09 17:56 ` Matthieu Longo
2026-03-13 18:09 ` Tom Tromey
2026-03-23 10:28 ` [PATCH v3 0/7] gdb: more fixes for Python limited C API support Matthieu Longo
7 siblings, 1 reply; 25+ messages in thread
From: Matthieu Longo @ 2026-03-09 17:56 UTC (permalink / raw)
To: gdb-patches, Tom Tromey; +Cc: Matthieu Longo
Python extension objects that support __dict__ must inherit from
gdbpy_dict_wrapper, a wrapper class that stores the PyObject
corresponding to the __dict__ attribute. Because this dictionary
is not managed directly by Python, each extension objects must
provide appropriate accessors so that attributes stored in __dict__
are looked up correctly.
Currently, management of this dictionary is not centralized, and
each Python extension object implements its own logic to create,
access, and destroy it.
A previous patch centralized the creation logic; this patch focuses
on centralizing the access to the dictionary. It introduces two new
macros:
- gdbpy_dict_wrapper_cfg_dict_getter, which defines a getter for
__dict__.
- gdbpy_dict_wrapper_getsetattro, which sets tp_getattro and
tp_setattro in PyTypeObject to gdb_py_generic_getattro and
gdb_py_generic_setattro, respectively. These helpers already
centralizes attribute access for Python extension objects having
a __dict__.
Note: this macro will eventually be removed once Python extension
types are migrated to heap-allocated types.
---
gdb/python/py-corefile.c | 6 ++----
gdb/python/py-event.c | 6 ++----
gdb/python/py-inferior.c | 6 ++----
gdb/python/py-infthread.c | 6 ++----
gdb/python/py-objfile.c | 6 ++----
gdb/python/py-progspace.c | 6 ++----
gdb/python/py-ref.h | 18 ++++++++++++++++--
gdb/python/py-type.c | 8 +++-----
8 files changed, 31 insertions(+), 31 deletions(-)
diff --git a/gdb/python/py-corefile.c b/gdb/python/py-corefile.c
index 0c2821cb07e..1daeabb5915 100644
--- a/gdb/python/py-corefile.c
+++ b/gdb/python/py-corefile.c
@@ -500,8 +500,7 @@ GDBPY_INITIALIZE_FILE (gdbpy_initialize_corefile);
static gdb_PyGetSetDef corefile_getset[] =
{
- { "__dict__", gdb_py_generic_dict_getter, nullptr,
- "The __dict__ for the gdb.Corefile.", nullptr },
+ gdbpy_dict_wrapper_cfg_dict_getter ("corefile"),
{ "filename", cfpy_get_filename, nullptr,
"The filename of a valid Corefile object.", nullptr },
{ nullptr }
@@ -537,8 +536,7 @@ PyTypeObject corefile_object_type =
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
- gdb_py_generic_getattro, /*tp_getattro*/
- gdb_py_generic_setattro, /*tp_setattro*/
+ gdbpy_dict_wrapper_getsetattro,
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
"GDB corefile object", /* tp_doc */
diff --git a/gdb/python/py-event.c b/gdb/python/py-event.c
index 8192d1cefa0..a7aa46dcb6f 100644
--- a/gdb/python/py-event.c
+++ b/gdb/python/py-event.c
@@ -100,8 +100,7 @@ GDBPY_INITIALIZE_FILE (gdbpy_initialize_event);
static gdb_PyGetSetDef event_object_getset[] =
{
- { "__dict__", gdb_py_generic_dict_getter, NULL,
- "The __dict__ for this event.", NULL },
+ gdbpy_dict_wrapper_cfg_dict_getter ("event"),
{ NULL }
};
@@ -123,8 +122,7 @@ PyTypeObject event_object_type =
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
- gdb_py_generic_getattro, /* tp_getattro */
- gdb_py_generic_setattro, /* tp_setattro */
+ gdbpy_dict_wrapper_getsetattro,
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
"GDB event object", /* tp_doc */
diff --git a/gdb/python/py-inferior.c b/gdb/python/py-inferior.c
index eaf224fe808..8df692f2545 100644
--- a/gdb/python/py-inferior.c
+++ b/gdb/python/py-inferior.c
@@ -1087,8 +1087,7 @@ GDBPY_INITIALIZE_FILE (gdbpy_initialize_inferior);
static gdb_PyGetSetDef inferior_object_getset[] =
{
- { "__dict__", gdb_py_generic_dict_getter, nullptr,
- "The __dict__ for this inferior.", nullptr },
+ gdbpy_dict_wrapper_cfg_dict_getter ("inferior"),
{ "arguments", infpy_get_args, infpy_set_args,
"Arguments to this program.", nullptr },
{ "num", infpy_get_num, NULL, "ID of inferior, as assigned by GDB.", NULL },
@@ -1170,8 +1169,7 @@ PyTypeObject inferior_object_type =
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
- gdb_py_generic_getattro, /* tp_getattro */
- gdb_py_generic_setattro, /* tp_setattro */
+ gdbpy_dict_wrapper_getsetattro,
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
"GDB inferior object", /* tp_doc */
diff --git a/gdb/python/py-infthread.c b/gdb/python/py-infthread.c
index 7afec0112fb..ae58936d99b 100644
--- a/gdb/python/py-infthread.c
+++ b/gdb/python/py-infthread.c
@@ -415,8 +415,7 @@ GDBPY_INITIALIZE_FILE (gdbpy_initialize_thread);
static gdb_PyGetSetDef thread_object_getset[] =
{
- { "__dict__", gdb_py_generic_dict_getter, nullptr,
- "The __dict__ for this thread.", nullptr },
+ gdbpy_dict_wrapper_cfg_dict_getter ("thread"),
{ "name", thpy_get_name, thpy_set_name,
"The name of the thread, as set by the user or the OS.", NULL },
{ "details", thpy_get_details, NULL,
@@ -479,8 +478,7 @@ PyTypeObject thread_object_type =
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
- gdb_py_generic_getattro, /*tp_getattro*/
- gdb_py_generic_setattro, /*tp_setattro*/
+ gdbpy_dict_wrapper_getsetattro,
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
"GDB thread object", /* tp_doc */
diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c
index daf1b44747c..0002425f2c8 100644
--- a/gdb/python/py-objfile.c
+++ b/gdb/python/py-objfile.c
@@ -725,8 +725,7 @@ Look up a static-linkage global symbol in this objfile and return it." },
static gdb_PyGetSetDef objfile_getset[] =
{
- { "__dict__", gdb_py_generic_dict_getter, NULL,
- "The __dict__ for this objfile.", NULL },
+ gdbpy_dict_wrapper_cfg_dict_getter ("objfile"),
{ "filename", objfpy_get_filename, NULL,
"The objfile's filename, or None.", NULL },
{ "username", objfpy_get_username, NULL,
@@ -771,8 +770,7 @@ PyTypeObject objfile_object_type =
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
- gdb_py_generic_getattro, /*tp_getattro*/
- gdb_py_generic_setattro, /*tp_setattro*/
+ gdbpy_dict_wrapper_getsetattro,
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
"GDB objfile object", /* tp_doc */
diff --git a/gdb/python/py-progspace.c b/gdb/python/py-progspace.c
index d1a8479588f..37b92a64634 100644
--- a/gdb/python/py-progspace.c
+++ b/gdb/python/py-progspace.c
@@ -751,8 +751,7 @@ GDBPY_INITIALIZE_FILE (gdbpy_initialize_pspace);
static gdb_PyGetSetDef pspace_getset[] =
{
- { "__dict__", gdb_py_generic_dict_getter, NULL,
- "The __dict__ for this progspace.", NULL },
+ gdbpy_dict_wrapper_cfg_dict_getter ("progspace"),
{ "filename", pspy_get_filename, NULL,
"The filename of the progspace's main symbol file, or None.", nullptr },
{ "symbol_file", pspy_get_symbol_file, nullptr,
@@ -814,8 +813,7 @@ PyTypeObject pspace_object_type =
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
- gdb_py_generic_getattro, /*tp_getattro*/
- gdb_py_generic_setattro, /*tp_setattro*/
+ gdbpy_dict_wrapper_getsetattro,
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
"GDB progspace object", /* tp_doc */
diff --git a/gdb/python/py-ref.h b/gdb/python/py-ref.h
index f0cdb38498e..b2e3b87e59f 100644
--- a/gdb/python/py-ref.h
+++ b/gdb/python/py-ref.h
@@ -50,8 +50,7 @@ template<typename T = PyObject> using gdbpy_ref
Access to the dict requires a custom getter defined via PyGetSetDef.
gdb_PyGetSetDef my_object_getset[] =
{
- { "__dict__", gdb_py_generic_dict_getter, nullptr,
- "The __dict__ for this object.", nullptr },
+ gdbpy_dict_wrapper_cfg_dict_getter ("object"),
...
{ nullptr }
};
@@ -78,6 +77,21 @@ struct gdbpy_dict_wrapper : public PyObject
return &wrapper->dict;
}
+ #define gdbpy_dict_wrapper_cfg_dict_getter(object_name) \
+ { \
+ "__dict__", /* name */ \
+ (getter) gdb_py_generic_dict_getter, \
+ (setter) nullptr, \
+ "The __dict__ for this " object_name ".", /* doc */ \
+ nullptr, /* closure */ \
+ }
+
+ #define gdbpy_dict_wrapper_getsetattro\
+ /*tp_getattro*/ \
+ gdb_py_generic_getattro, \
+ /*tp_setattro*/ \
+ gdb_py_generic_setattro
+
bool
allocate_dict ()
{
diff --git a/gdb/python/py-type.c b/gdb/python/py-type.c
index 0893da5aec9..263d48a9365 100644
--- a/gdb/python/py-type.c
+++ b/gdb/python/py-type.c
@@ -1700,9 +1700,8 @@ PyTypeObject type_object_type =
static gdb_PyGetSetDef field_object_getset[] =
{
- { "__dict__", gdb_py_generic_dict_getter, NULL,
- "The __dict__ for this field.", NULL },
- { NULL }
+ gdbpy_dict_wrapper_cfg_dict_getter ("field"),
+ { nullptr }
};
PyTypeObject field_object_type =
@@ -1723,8 +1722,7 @@ PyTypeObject field_object_type =
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
- gdb_py_generic_getattro, /*tp_getattro*/
- gdb_py_generic_setattro, /*tp_setattro*/
+ gdbpy_dict_wrapper_getsetattro,
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
"GDB field object", /* tp_doc */
--
2.53.0
^ permalink raw reply [flat|nested] 25+ messages in thread* Re: [PATCH v3 7/7] gdb/python: add accessor helpers for __dict__ in Python extension objects
2026-03-09 17:56 ` [PATCH v3 7/7] gdb/python: add accessor helpers for __dict__ in Python extension objects Matthieu Longo
@ 2026-03-13 18:09 ` Tom Tromey
0 siblings, 0 replies; 25+ messages in thread
From: Tom Tromey @ 2026-03-13 18:09 UTC (permalink / raw)
To: Matthieu Longo; +Cc: gdb-patches, Tom Tromey
>>>>> Matthieu Longo <matthieu.longo@arm.com> writes:
> Python extension objects that support __dict__ must inherit from
> gdbpy_dict_wrapper, a wrapper class that stores the PyObject
> corresponding to the __dict__ attribute. Because this dictionary
> is not managed directly by Python, each extension objects must
> provide appropriate accessors so that attributes stored in __dict__
> are looked up correctly.
> + #define gdbpy_dict_wrapper_cfg_dict_getter(object_name) \
> + { \
Don't indent the '#define'
> + #define gdbpy_dict_wrapper_getsetattro\
> + /*tp_getattro*/ \
Here too.
Ok with that fixup.
Approved-By: Tom Tromey <tom@tromey.com>
Tom
^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: [PATCH v3 0/7] gdb: more fixes for Python limited C API support
2026-03-09 17:56 [PATCH v3 0/7] gdb: more fixes for Python limited C API support Matthieu Longo
` (6 preceding siblings ...)
2026-03-09 17:56 ` [PATCH v3 7/7] gdb/python: add accessor helpers for __dict__ in Python extension objects Matthieu Longo
@ 2026-03-23 10:28 ` Matthieu Longo
7 siblings, 0 replies; 25+ messages in thread
From: Matthieu Longo @ 2026-03-23 10:28 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
On 09/03/2026 17:56, Matthieu Longo wrote:
> This patch series fixes some of the issues encountered while enabling the Python limited C API in GDB.
>
> Diff with revision 2: https://inbox.sourceware.org/gdb-patches/20260303161659.397427-1-matthieu.longo@arm.com/
> - patch 1,3,7 of revision 2 were merged into master.
> - patches 1,3,4,5,6,7 were already present in revision 2, and I addressed comments from Tom Tromey of revision 2.
> - patch 2 makes the configure script fail if the developer selects the build against the Python limited API with an unsupported version of Python.
>
> 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
>
>
> Matthieu Longo (7):
> gdb: introduce rgb_color type to simplify existing code
> gdb: fail configure if Python version is too old for limited API
> gdb: add new helpers for retrieving a type's fully qualified name
> gdb/python: allow ref_ptr<T, Policy>::new_reference to accept subclasses of T
> gdb/python: flatten functions calling PyObject_New and use gdbpy_ref
> gdb/python: add gdbpy_dict_wrapper:allocate_dict helper
> gdb/python: add accessor helpers for __dict__ in Python extension objects
>
Hi Tom,
FYI I merged all the patches into master, except 2 and 3 for which I am still waiting for your review.
Regards,
Matthieu
^ permalink raw reply [flat|nested] 25+ messages in thread