From 04d871dd0623ee007b95be87b1186ac54f39f5fd Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 5 Aug 2026 10:39:34 +0300 Subject: [PATCH] gh-155218: Generate the option group flags in a stable order They were emitted in the iteration order of a set of group identifiers. The identifiers of the groups before the required parameters are small negative integers, -1 and -2 have the same hash, so their order depended on the size of Py_hash_t, and the code generated on a 32-bit platform differed from the checked in one. They are now emitted in the order of the parameters. Co-Authored-By: Claude Opus 5 (1M context) --- ...6-08-05-14-02-55.gh-issue-155218.Nq4xZv.rst | 2 ++ Modules/clinic/_cursesmodule.c.h | 18 +++++++++--------- Tools/clinic/libclinic/clanguage.py | 6 ++++-- 3 files changed, 15 insertions(+), 11 deletions(-) create mode 100644 Misc/NEWS.d/next/Tools-Demos/2026-08-05-14-02-55.gh-issue-155218.Nq4xZv.rst diff --git a/Misc/NEWS.d/next/Tools-Demos/2026-08-05-14-02-55.gh-issue-155218.Nq4xZv.rst b/Misc/NEWS.d/next/Tools-Demos/2026-08-05-14-02-55.gh-issue-155218.Nq4xZv.rst new file mode 100644 index 00000000000000..909efc558693c0 --- /dev/null +++ b/Misc/NEWS.d/next/Tools-Demos/2026-08-05-14-02-55.gh-issue-155218.Nq4xZv.rst @@ -0,0 +1,2 @@ +Fix Argument Clinic generating the flags of the optional groups in +different order on 32-bit and 64-bit platforms. diff --git a/Modules/clinic/_cursesmodule.c.h b/Modules/clinic/_cursesmodule.c.h index dfd589ba45089e..58087416796f8f 100644 --- a/Modules/clinic/_cursesmodule.c.h +++ b/Modules/clinic/_cursesmodule.c.h @@ -247,8 +247,8 @@ _curses_window_addch(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "iiOO&:addch", &y, &x, &ch, attr_converter, &attr)) { goto exit; } - group_right_1 = 1; group_left_1 = 1; + group_right_1 = 1; break; default: PyErr_SetString(PyExc_TypeError, "_curses.window.addch requires 1 to 4 arguments"); @@ -319,8 +319,8 @@ _curses_window_addstr(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "iiOO&:addstr", &y, &x, &str, attr_converter, &attr)) { goto exit; } - group_right_1 = 1; group_left_1 = 1; + group_right_1 = 1; break; default: PyErr_SetString(PyExc_TypeError, "_curses.window.addstr requires 1 to 4 arguments"); @@ -394,8 +394,8 @@ _curses_window_addnstr(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "iiOiO&:addnstr", &y, &x, &str, &n, attr_converter, &attr)) { goto exit; } - group_right_1 = 1; group_left_1 = 1; + group_right_1 = 1; break; default: PyErr_SetString(PyExc_TypeError, "_curses.window.addnstr requires 2 to 5 arguments"); @@ -1451,8 +1451,8 @@ _curses_window_hline(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "iiOiO&:hline", &y, &x, &ch, &n, attr_converter, &attr)) { goto exit; } - group_right_1 = 1; group_left_1 = 1; + group_right_1 = 1; break; default: PyErr_SetString(PyExc_TypeError, "_curses.window.hline requires 2 to 5 arguments"); @@ -1521,8 +1521,8 @@ _curses_window_insch(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "iiOO&:insch", &y, &x, &ch, attr_converter, &attr)) { goto exit; } - group_right_1 = 1; group_left_1 = 1; + group_right_1 = 1; break; default: PyErr_SetString(PyExc_TypeError, "_curses.window.insch requires 1 to 4 arguments"); @@ -1640,8 +1640,8 @@ _curses_window_insstr(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "iiOO&:insstr", &y, &x, &str, attr_converter, &attr)) { goto exit; } - group_right_1 = 1; group_left_1 = 1; + group_right_1 = 1; break; default: PyErr_SetString(PyExc_TypeError, "_curses.window.insstr requires 1 to 4 arguments"); @@ -1717,8 +1717,8 @@ _curses_window_insnstr(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "iiOiO&:insnstr", &y, &x, &str, &n, attr_converter, &attr)) { goto exit; } - group_right_1 = 1; group_left_1 = 1; + group_right_1 = 1; break; default: PyErr_SetString(PyExc_TypeError, "_curses.window.insnstr requires 2 to 5 arguments"); @@ -2328,8 +2328,8 @@ _curses_window_vline(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "iiOiO&:vline", &y, &x, &ch, &n, attr_converter, &attr)) { goto exit; } - group_right_1 = 1; group_left_1 = 1; + group_right_1 = 1; break; default: PyErr_SetString(PyExc_TypeError, "_curses.window.vline requires 2 to 5 arguments"); @@ -6234,4 +6234,4 @@ _curses_has_extended_color_support(PyObject *module, PyObject *Py_UNUSED(ignored #ifndef _CURSES_ASSUME_DEFAULT_COLORS_METHODDEF #define _CURSES_ASSUME_DEFAULT_COLORS_METHODDEF #endif /* !defined(_CURSES_ASSUME_DEFAULT_COLORS_METHODDEF) */ -/*[clinic end generated code: output=cb5525c88ae5c440 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=c1f85ec415c303bf input=a9049054013a1b77]*/ diff --git a/Tools/clinic/libclinic/clanguage.py b/Tools/clinic/libclinic/clanguage.py index a76fddb7602001..3ee06307441fac 100644 --- a/Tools/clinic/libclinic/clanguage.py +++ b/Tools/clinic/libclinic/clanguage.py @@ -341,7 +341,9 @@ def render_option_group_parsing( """) continue - group_ids = {p.group for p in subset} # eliminate duplicates + # A set would eliminate duplicates too, but the iteration + # order of small negative integers depends on the platform. + group_ids = dict.fromkeys(p.group for p in subset) d: dict[str, str | int] = {} d['count'] = count d['name'] = f.name @@ -356,7 +358,7 @@ def render_option_group_parsing( p.converter.parse_argument(parse_arguments) d['parse_arguments'] = ", ".join(parse_arguments) - group_ids.discard(0) + group_ids.pop(0, None) lines = "\n".join([ self.group_to_variable_name(g) + " = 1;" for g in group_ids