From 425b022675820b5d75f93c34ac816b4b8bfd0959 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 12 Jun 2026 15:26:09 +0200 Subject: [PATCH 01/10] gh-151218: Replace sys.flags in PyConfig_Set() PyConfig_Set() and sys.set_int_max_str_digits() now replace sys.flags, instead of modifying sys.flags in-place. Modifying sys.flags in-place can lead to data races when multiple threads are reading or writing sys.flags in parallel. Use _Py_atomic functions to get and set max_str_digits members. --- Lib/test/test_capi/test_config.py | 2 + ...-06-12-15-30-25.gh-issue-151218.5M_nv8.rst | 3 + Python/sysmodule.c | 80 +++++++++++++++---- 3 files changed, 68 insertions(+), 17 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-06-12-15-30-25.gh-issue-151218.5M_nv8.rst diff --git a/Lib/test/test_capi/test_config.py b/Lib/test/test_capi/test_config.py index f10ad50d3bea7e9..c750a6c2a477abd 100644 --- a/Lib/test/test_capi/test_config.py +++ b/Lib/test/test_capi/test_config.py @@ -356,9 +356,11 @@ def expect_bool_not(value): for value in new_values: expected, expect_flag = expect_func(value) + old_flags = sys.flags config_set(name, value) self.assertEqual(config_get(name), expected) self.assertEqual(getattr(sys.flags, sys_flag), expect_flag) + self.assertIsNot(sys.flags, old_flags) if name == "write_bytecode": self.assertEqual(getattr(sys, "dont_write_bytecode"), expect_flag) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-12-15-30-25.gh-issue-151218.5M_nv8.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-12-15-30-25.gh-issue-151218.5M_nv8.rst new file mode 100644 index 000000000000000..8183539d9cee93d --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-12-15-30-25.gh-issue-151218.5M_nv8.rst @@ -0,0 +1,3 @@ +:c:func:`PyConfig_Set()` and :func:`sys.set_int_max_str_digits` now replace +:data:`sys.flags`, instead of modifying :data:`sys.flags` in-place. Patch by +Victor Stinner. diff --git a/Python/sysmodule.c b/Python/sysmodule.c index f7e28086d84fab9..aa81d69e97c0601 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -1874,7 +1874,8 @@ sys_get_int_max_str_digits_impl(PyObject *module) /*[clinic end generated code: output=0042f5e8ae0e8631 input=77fb74e987ba7ecb]*/ { PyInterpreterState *interp = _PyInterpreterState_GET(); - return PyLong_FromLong(interp->long_state.max_str_digits); + int maxdigits = _Py_atomic_load_int(&interp->long_state.max_str_digits); + return PyLong_FromLong(maxdigits); } @@ -3490,14 +3491,50 @@ sys_set_flag(PyObject *flags, Py_ssize_t pos, PyObject *value) int _PySys_SetFlagObj(Py_ssize_t pos, PyObject *value) { - PyObject *flags = PySys_GetAttrString("flags"); - if (flags == NULL) { - return -1; + PyObject *old_flags = NULL; + PyObject *new_flags = NULL; + PyObject *flags_str = NULL; + + flags_str = PyUnicode_FromString("flags"); + if (flags_str == NULL) { + goto error; } - sys_set_flag(flags, pos, value); - Py_DECREF(flags); - return 0; + old_flags = PySys_GetAttr(flags_str); + if (old_flags == NULL) { + goto error; + } + + new_flags = PyStructSequence_New(&FlagsType); + if (new_flags == NULL) { + goto error; + } + + for (Py_ssize_t i=0; i < (Py_ssize_t)(Py_ARRAY_LENGTH(flags_fields) - 1); i++) { + if (i != pos) { + PyObject *old_value; + old_value = PyStructSequence_GET_ITEM(old_flags, i); // borrowed ref + if (old_value == NULL) { + goto error; + } + sys_set_flag(new_flags, i, old_value); + } + else { + sys_set_flag(new_flags, pos, value); + } + } + + int res = _PySys_SetAttr(flags_str, new_flags); + Py_DECREF(flags_str); + Py_DECREF(old_flags); + Py_DECREF(new_flags); + return res; + +error: + Py_DECREF(flags_str); + Py_DECREF(old_flags); + Py_DECREF(new_flags); + return -1; } @@ -3521,8 +3558,6 @@ set_flags_from_config(PyInterpreterState *interp, PyObject *flags) const PyPreConfig *preconfig = &interp->runtime->preconfig; const PyConfig *config = _PyInterpreterState_GetConfig(interp); - // _PySys_UpdateConfig() modifies sys.flags in-place: - // Py_XDECREF() is needed in this case. Py_ssize_t pos = 0; #define SetFlagObj(expr) \ do { \ @@ -4153,16 +4188,27 @@ _PySys_UpdateConfig(PyThreadState *tstate) #undef COPY_LIST #undef COPY_WSTR - // sys.flags - PyObject *flags = PySys_GetAttrString("flags"); - if (flags == NULL) { + // replace sys.flags + PyObject *new_flags = PyStructSequence_New(&FlagsType); + if (new_flags == NULL) { return -1; } - if (set_flags_from_config(interp, flags) < 0) { - Py_DECREF(flags); + if (set_flags_from_config(interp, new_flags) < 0) { + Py_DECREF(new_flags); + return -1; + } + + PyObject *flags_str = PyUnicode_FromString("flags"); + if (flags_str == NULL) { + Py_DECREF(new_flags); + return -1; + } + res = _PySys_SetAttr(flags_str, new_flags); + Py_DECREF(new_flags); + Py_DECREF(flags_str); + if (res < 0) { return -1; } - Py_DECREF(flags); SET_SYS("dont_write_bytecode", PyBool_FromLong(!config->write_bytecode)); @@ -4675,7 +4721,7 @@ _PySys_SetIntMaxStrDigits(int maxdigits) // Set PyInterpreterState.long_state.max_str_digits // and PyInterpreterState.config.int_max_str_digits. PyInterpreterState *interp = _PyInterpreterState_GET(); - interp->long_state.max_str_digits = maxdigits; - interp->config.int_max_str_digits = maxdigits; + _Py_atomic_store_int(&interp->long_state.max_str_digits, maxdigits); + _Py_atomic_store_int(&interp->config.int_max_str_digits, maxdigits); return 0; } From 19e863c226f704d0999d1b2eac727c9a0fe0fa6a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 15 Jun 2026 16:54:31 +0200 Subject: [PATCH 02/10] Add unit tests Add test_sys to "./python -m test --tsan" tests. --- Lib/test/libregrtest/tsan.py | 1 + Lib/test/test_sys.py | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/Lib/test/libregrtest/tsan.py b/Lib/test/libregrtest/tsan.py index bacfe5e21ba0b7d..11abfb72983e1ce 100644 --- a/Lib/test/libregrtest/tsan.py +++ b/Lib/test/libregrtest/tsan.py @@ -20,6 +20,7 @@ 'test_socket', 'test_sqlite3', 'test_ssl', + 'test_sys', 'test_syslog', 'test_thread', 'test_thread_local_bytecode', diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 02c70403185f60d..fec1f283f60be03 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1349,6 +1349,40 @@ def test_pystats(self): def test_disable_gil_abi(self): self.assertEqual('t' in sys.abiflags, support.Py_GIL_DISABLED) + def test_int_max_str_digits(self): + old_limit = sys.get_int_max_str_digits() + self.assertIsInstance(old_limit, int) + self.assertGreaterEqual(old_limit, 0) + self.addCleanup(sys.set_int_max_str_digits, old_limit) + + sys.set_int_max_str_digits(0) + self.assertEqual(sys.get_int_max_str_digits(), 0) + + sys.set_int_max_str_digits(2_048) + self.assertEqual(sys.get_int_max_str_digits(), 2_048) + + with self.assertRaises(ValueError): + # the minimum is 640 digits + sys.set_int_max_str_digits(5) + with self.assertRaises(ValueError): + sys.set_int_max_str_digits(-2) + with self.assertRaises(TypeError): + sys.set_int_max_str_digits(2_048.0) + + def test_int_max_str_digits_thread(self): + # gh-151218: Check that it's safe to call set_int_max_str_digits() + # in parallel. Previously, this test triggered warnings in TSan + # on a free threaded build. + + old_limit = sys.get_int_max_str_digits() + self.addCleanup(sys.set_int_max_str_digits, old_limit) + + def worker(): + for i in range (20_000): + sys.set_int_max_str_digits(4300 + (i & 7)) + + threading_helper.run_concurrently(worker, nthreads=4) + @test.support.cpython_only @test.support.force_not_colorized_test_class From a66a331f9ef54c4ab76360bbcf5d2e1f13f6fb92 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 15 Jun 2026 17:09:40 +0200 Subject: [PATCH 03/10] Move test to test_free_threading.test_sys --- Lib/test/libregrtest/tsan.py | 1 - Lib/test/test_free_threading/test_sys.py | 23 +++++++++++++++++++++++ Lib/test/test_sys.py | 14 -------------- 3 files changed, 23 insertions(+), 15 deletions(-) create mode 100644 Lib/test/test_free_threading/test_sys.py diff --git a/Lib/test/libregrtest/tsan.py b/Lib/test/libregrtest/tsan.py index 11abfb72983e1ce..bacfe5e21ba0b7d 100644 --- a/Lib/test/libregrtest/tsan.py +++ b/Lib/test/libregrtest/tsan.py @@ -20,7 +20,6 @@ 'test_socket', 'test_sqlite3', 'test_ssl', - 'test_sys', 'test_syslog', 'test_thread', 'test_thread_local_bytecode', diff --git a/Lib/test/test_free_threading/test_sys.py b/Lib/test/test_free_threading/test_sys.py new file mode 100644 index 000000000000000..335b66eca4b2024 --- /dev/null +++ b/Lib/test/test_free_threading/test_sys.py @@ -0,0 +1,23 @@ +import sys +import unittest +from test.support import threading_helper + + +class SysModuleTest(unittest.TestCase): + def test_int_max_str_digits_thread(self): + # gh-151218: Check that it's safe to call set_int_max_str_digits() + # in parallel. Previously, this test triggered warnings in TSan + # on a free threaded build. + + old_limit = sys.get_int_max_str_digits() + self.addCleanup(sys.set_int_max_str_digits, old_limit) + + def worker(): + for i in range (20_000): + sys.set_int_max_str_digits(4300 + (i & 7)) + + threading_helper.run_concurrently(worker, nthreads=4) + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index fec1f283f60be03..f40da0b79aa4790 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1369,20 +1369,6 @@ def test_int_max_str_digits(self): with self.assertRaises(TypeError): sys.set_int_max_str_digits(2_048.0) - def test_int_max_str_digits_thread(self): - # gh-151218: Check that it's safe to call set_int_max_str_digits() - # in parallel. Previously, this test triggered warnings in TSan - # on a free threaded build. - - old_limit = sys.get_int_max_str_digits() - self.addCleanup(sys.set_int_max_str_digits, old_limit) - - def worker(): - for i in range (20_000): - sys.set_int_max_str_digits(4300 + (i & 7)) - - threading_helper.run_concurrently(worker, nthreads=4) - @test.support.cpython_only @test.support.force_not_colorized_test_class From 4ca409d77897507570e2a181cb1c2a23276f0580 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 15 Jun 2026 17:11:15 +0200 Subject: [PATCH 04/10] Complete Changelog entry --- .../2026-06-12-15-30-25.gh-issue-151218.5M_nv8.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-12-15-30-25.gh-issue-151218.5M_nv8.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-12-15-30-25.gh-issue-151218.5M_nv8.rst index 8183539d9cee93d..37404cb44ba9d7b 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-12-15-30-25.gh-issue-151218.5M_nv8.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-12-15-30-25.gh-issue-151218.5M_nv8.rst @@ -1,3 +1,3 @@ :c:func:`PyConfig_Set()` and :func:`sys.set_int_max_str_digits` now replace -:data:`sys.flags`, instead of modifying :data:`sys.flags` in-place. Patch by -Victor Stinner. +:data:`sys.flags` (create a new object), instead of modifying :data:`sys.flags` +in-place. Patch by Victor Stinner. From f1142949701326dd165e43ac104defe5af7244fd Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 16 Jun 2026 13:28:33 +0200 Subject: [PATCH 05/10] Address picnixz's review --- Doc/c-api/init_config.rst | 4 ++++ ...026-06-12-15-30-25.gh-issue-151218.5M_nv8.rst | 2 +- Objects/longobject.c | 6 +++--- Python/initconfig.c | 3 ++- Python/pylifecycle.c | 3 ++- Python/sysmodule.c | 16 ++++------------ 6 files changed, 16 insertions(+), 18 deletions(-) diff --git a/Doc/c-api/init_config.rst b/Doc/c-api/init_config.rst index 209e48767ccfd6c..b3a87df15695906 100644 --- a/Doc/c-api/init_config.rst +++ b/Doc/c-api/init_config.rst @@ -621,6 +621,10 @@ Some options are read from the :mod:`sys` attributes. For example, the option .. audit-event:: cpython.PyConfig_Set name,value c.PyConfig_Set + .. versionchanged:: next + The function now replaces :data:`sys.flags` (create a new object), + instead of modifying :data:`sys.flags` in-place. + .. versionadded:: 3.14 diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-12-15-30-25.gh-issue-151218.5M_nv8.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-12-15-30-25.gh-issue-151218.5M_nv8.rst index 37404cb44ba9d7b..46539efc373eb0d 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-12-15-30-25.gh-issue-151218.5M_nv8.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-12-15-30-25.gh-issue-151218.5M_nv8.rst @@ -1,3 +1,3 @@ -:c:func:`PyConfig_Set()` and :func:`sys.set_int_max_str_digits` now replace +:c:func:`PyConfig_Set` and :func:`sys.set_int_max_str_digits` now replace :data:`sys.flags` (create a new object), instead of modifying :data:`sys.flags` in-place. Patch by Victor Stinner. diff --git a/Objects/longobject.c b/Objects/longobject.c index 6e6011cb19aab5f..7a38ae8ea5a36f0 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -2125,7 +2125,7 @@ long_to_decimal_string_internal(PyObject *aa, if (size_a >= 10 * _PY_LONG_MAX_STR_DIGITS_THRESHOLD / (3 * PyLong_SHIFT) + 2) { PyInterpreterState *interp = _PyInterpreterState_GET(); - int max_str_digits = interp->long_state.max_str_digits; + int max_str_digits = _Py_atomic_load_int(&interp->long_state.max_str_digits); if ((max_str_digits > 0) && (max_str_digits / (3 * PyLong_SHIFT) <= (size_a - 11) / 10)) { PyErr_Format(PyExc_ValueError, _MAX_STR_DIGITS_ERROR_FMT_TO_STR, @@ -2206,7 +2206,7 @@ long_to_decimal_string_internal(PyObject *aa, } if (strlen > _PY_LONG_MAX_STR_DIGITS_THRESHOLD) { PyInterpreterState *interp = _PyInterpreterState_GET(); - int max_str_digits = interp->long_state.max_str_digits; + int max_str_digits = _Py_atomic_load_int(&interp->long_state.max_str_digits); Py_ssize_t strlen_nosign = strlen - negative; if ((max_str_digits > 0) && (strlen_nosign > max_str_digits)) { Py_DECREF(scratch); @@ -3021,7 +3021,7 @@ long_from_string_base(const char **str, int base, PyLongObject **res) * quadratic algorithm. */ if (digits > _PY_LONG_MAX_STR_DIGITS_THRESHOLD) { PyInterpreterState *interp = _PyInterpreterState_GET(); - int max_str_digits = interp->long_state.max_str_digits; + int max_str_digits = _Py_atomic_load_int(&interp->long_state.max_str_digits); if ((max_str_digits > 0) && (digits > max_str_digits)) { PyErr_Format(PyExc_ValueError, _MAX_STR_DIGITS_ERROR_FMT_TO_INT, max_str_digits, digits); diff --git a/Python/initconfig.c b/Python/initconfig.c index eb0a5ef7aa01365..f653c00d1f0bed8 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -4641,7 +4641,8 @@ config_get(const PyConfig *config, const PyConfigSpec *spec, if (strcmp(spec->name, "int_max_str_digits") == 0) { PyInterpreterState *interp = _PyInterpreterState_GET(); - return PyLong_FromLong(interp->long_state.max_str_digits); + int maxdigits = _Py_atomic_load_int(&interp->long_state.max_str_digits); + return PyLong_FromLong(maxdigits); } } diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 2f7f10f523c2c1d..01ca459b2eb2b8f 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -429,7 +429,8 @@ interpreter_update_config(PyThreadState *tstate, int only_update_path_config) } } - tstate->interp->long_state.max_str_digits = config->int_max_str_digits; + _Py_atomic_store_int(&tstate->interp->long_state.max_str_digits, + config->int_max_str_digits); // Update the sys module for the new configuration if (_PySys_UpdateConfig(tstate) < 0) { diff --git a/Python/sysmodule.c b/Python/sysmodule.c index aa81d69e97c0601..099efd9244914d7 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -3491,16 +3491,10 @@ sys_set_flag(PyObject *flags, Py_ssize_t pos, PyObject *value) int _PySys_SetFlagObj(Py_ssize_t pos, PyObject *value) { - PyObject *old_flags = NULL; PyObject *new_flags = NULL; - PyObject *flags_str = NULL; + PyObject *flags_str = &_Py_ID(flags); // immortal ref - flags_str = PyUnicode_FromString("flags"); - if (flags_str == NULL) { - goto error; - } - - old_flags = PySys_GetAttr(flags_str); + PyObject *old_flags = PySys_GetAttr(flags_str); if (old_flags == NULL) { goto error; } @@ -3525,15 +3519,13 @@ _PySys_SetFlagObj(Py_ssize_t pos, PyObject *value) } int res = _PySys_SetAttr(flags_str, new_flags); - Py_DECREF(flags_str); Py_DECREF(old_flags); Py_DECREF(new_flags); return res; error: - Py_DECREF(flags_str); - Py_DECREF(old_flags); - Py_DECREF(new_flags); + Py_XDECREF(old_flags); + Py_XDECREF(new_flags); return -1; } From e5280f13305e77c5059cf58a477628d41223c599 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 16 Jun 2026 13:41:20 +0200 Subject: [PATCH 06/10] Add get_int_max_str_digits() to the test --- Lib/test/test_free_threading/test_sys.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_free_threading/test_sys.py b/Lib/test/test_free_threading/test_sys.py index 335b66eca4b2024..37b53bd723fd767 100644 --- a/Lib/test/test_free_threading/test_sys.py +++ b/Lib/test/test_free_threading/test_sys.py @@ -5,18 +5,23 @@ class SysModuleTest(unittest.TestCase): def test_int_max_str_digits_thread(self): - # gh-151218: Check that it's safe to call set_int_max_str_digits() - # in parallel. Previously, this test triggered warnings in TSan - # on a free threaded build. + # gh-151218: Check that it's safe to call get_int_max_str_digits() and + # set_int_max_str_digits() in parallel. Previously, this test triggered + # warnings in TSan on a free threaded build. old_limit = sys.get_int_max_str_digits() self.addCleanup(sys.set_int_max_str_digits, old_limit) - def worker(): - for i in range (20_000): - sys.set_int_max_str_digits(4300 + (i & 7)) + def worker(worker_id): + if not worker_id: + for i in range (20_000): + sys.get_int_max_str_digits() + else: + for i in range (20_000): + sys.set_int_max_str_digits(4300 + (i & 7)) - threading_helper.run_concurrently(worker, nthreads=4) + workers = [lambda: worker(i) for i in range(5)] + threading_helper.run_concurrently(workers) if __name__ == "__main__": From 770d385b6b888d42a511155045e9c7f2f9435e77 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 16 Jun 2026 13:47:54 +0200 Subject: [PATCH 07/10] Move versionadded --- Doc/c-api/init_config.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/c-api/init_config.rst b/Doc/c-api/init_config.rst index b3a87df15695906..d6b9837987a3999 100644 --- a/Doc/c-api/init_config.rst +++ b/Doc/c-api/init_config.rst @@ -621,12 +621,12 @@ Some options are read from the :mod:`sys` attributes. For example, the option .. audit-event:: cpython.PyConfig_Set name,value c.PyConfig_Set + .. versionadded:: 3.14 + .. versionchanged:: next The function now replaces :data:`sys.flags` (create a new object), instead of modifying :data:`sys.flags` in-place. - .. versionadded:: 3.14 - .. _pyconfig_api: From df57cf2605625d8a4e3fcd713607b3434bdb4cf6 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 16 Jun 2026 13:52:56 +0200 Subject: [PATCH 08/10] Use also _Py_ID() in _PySys_UpdateConfig() Update also outdated comment. --- Python/sysmodule.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 099efd9244914d7..4234efb187b1a98 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -4060,7 +4060,7 @@ _PySys_InitCore(PyThreadState *tstate, PyObject *sysdict) /* implementation */ SET_SYS("implementation", make_impl_info(version_info)); - // sys.flags: updated in-place later by _PySys_UpdateConfig() + // sys.flags: updated later by _PySys_UpdateConfig() ENSURE_INFO_TYPE(FlagsType, flags_desc); SET_SYS("flags", make_flags(tstate->interp)); @@ -4190,14 +4190,8 @@ _PySys_UpdateConfig(PyThreadState *tstate) return -1; } - PyObject *flags_str = PyUnicode_FromString("flags"); - if (flags_str == NULL) { - Py_DECREF(new_flags); - return -1; - } - res = _PySys_SetAttr(flags_str, new_flags); + res = _PySys_SetAttr(&_Py_ID(flags), new_flags); Py_DECREF(new_flags); - Py_DECREF(flags_str); if (res < 0) { return -1; } From 44bc1b2a7982d79fe760c0f554d0262246e4b8f6 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 16 Jun 2026 14:00:01 +0200 Subject: [PATCH 09/10] Update Python/sysmodule.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Python/sysmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 4234efb187b1a98..229d925305a0a89 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -3504,7 +3504,7 @@ _PySys_SetFlagObj(Py_ssize_t pos, PyObject *value) goto error; } - for (Py_ssize_t i=0; i < (Py_ssize_t)(Py_ARRAY_LENGTH(flags_fields) - 1); i++) { + for (Py_ssize_t i = 0; i < (Py_ssize_t)(Py_ARRAY_LENGTH(flags_fields) - 1); i++) { if (i != pos) { PyObject *old_value; old_value = PyStructSequence_GET_ITEM(old_flags, i); // borrowed ref From 1843290cd8560ec2098016fc4756ca5ce34a22d3 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 16 Jun 2026 17:51:32 +0200 Subject: [PATCH 10/10] PyStructSequence_GET_ITEM() cannot fail --- Python/sysmodule.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 229d925305a0a89..d9f7b9c449cfb94 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -3508,9 +3508,6 @@ _PySys_SetFlagObj(Py_ssize_t pos, PyObject *value) if (i != pos) { PyObject *old_value; old_value = PyStructSequence_GET_ITEM(old_flags, i); // borrowed ref - if (old_value == NULL) { - goto error; - } sys_set_flag(new_flags, i, old_value); } else {