From 72d29ea363f1515115753653aeca735a1a817a7f Mon Sep 17 00:00:00 2001 From: sunmy2019 <59365878+sunmy2019@users.noreply.github.com> Date: Tue, 31 Mar 2026 15:48:02 +0800 Subject: [PATCH 1/7] gh-146615: Fix crash in __get__() for METH_METHOD descriptors with invalid type argument (GH-146634) --- Lib/test/test_descr.py | 22 +++++++++++++++++++ ...1-06-35.gh-issue-146615.fix-method-get.rst | 3 +++ Objects/descrobject.c | 2 +- 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-03-31-01-06-35.gh-issue-146615.fix-method-get.rst diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 1d7669e4fa5c96..8a8e70214e27ae 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -1803,6 +1803,28 @@ class SubSpam(spam.spamlist): pass spam_cm.__get__(None, list) self.assertEqual(str(cm.exception), expected_errmsg) + @support.cpython_only + def test_method_get_meth_method_invalid_type(self): + # gh-146615: method_get() for METH_METHOD descriptors used to pass + # Py_TYPE(type)->tp_name as the %V fallback instead of the separate + # %s argument, causing a missing argument for %s and a crash. + # Verify the error message is correct when __get__() is called with a + # non-type as the second argument. + # + # METH_METHOD|METH_FASTCALL|METH_KEYWORDS is the only flag combination + # that enters the affected branch in method_get(). + import io + + obj = io.StringIO() + descr = io.TextIOBase.read + + with self.assertRaises(TypeError) as cm: + descr.__get__(obj, "not_a_type") + self.assertEqual( + str(cm.exception), + "descriptor 'read' needs a type, not 'str', as arg 2", + ) + def test_staticmethods(self): # Testing static methods... class C(object): diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-03-31-01-06-35.gh-issue-146615.fix-method-get.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-03-31-01-06-35.gh-issue-146615.fix-method-get.rst new file mode 100644 index 00000000000000..7a205f1d6dda61 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-03-31-01-06-35.gh-issue-146615.fix-method-get.rst @@ -0,0 +1,3 @@ +Fix a crash in :meth:`~object.__get__` for :c:expr:`METH_METHOD` descriptors +when an invalid (non-type) object is passed as the second argument. +Patch by Steven Sun. diff --git a/Objects/descrobject.c b/Objects/descrobject.c index 5ac4fbd812924c..517d9e9fa9d45d 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -150,7 +150,7 @@ method_get(PyObject *self, PyObject *obj, PyObject *type) } else { PyErr_Format(PyExc_TypeError, "descriptor '%V' needs a type, not '%s', as arg 2", - descr_name((PyDescrObject *)descr), + descr_name((PyDescrObject *)descr), "?", Py_TYPE(type)->tp_name); return NULL; } From 1c396e18218daa723b425af0781c5e762d7717c2 Mon Sep 17 00:00:00 2001 From: sunmy2019 <59365878+sunmy2019@users.noreply.github.com> Date: Tue, 31 Mar 2026 15:57:37 +0800 Subject: [PATCH 2/7] gh-146615: Fix format specifiers in extension modules (GH-146617) --- Modules/_asynciomodule.c | 6 +++--- Modules/_remote_debugging/asyncio.c | 2 +- Modules/_ssl.c | 14 +++++--------- Modules/_zoneinfo.c | 2 +- Modules/binascii.c | 4 ++-- Modules/socketmodule.c | 3 +-- 6 files changed, 13 insertions(+), 18 deletions(-) diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 826c0b25a362c2..bd294648ca222f 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -2244,7 +2244,7 @@ enter_task(_PyThreadStateImpl *ts, PyObject *loop, PyObject *task) PyExc_RuntimeError, "Cannot enter into task %R while another " \ "task %R is being executed.", - task, ts->asyncio_running_task, NULL); + task, ts->asyncio_running_task); return -1; } @@ -2265,7 +2265,7 @@ leave_task(_PyThreadStateImpl *ts, PyObject *loop, PyObject *task) PyExc_RuntimeError, "Invalid attempt to leave task %R while " \ "task %R is entered.", - task, ts->asyncio_running_task ? ts->asyncio_running_task : Py_None, NULL); + task, ts->asyncio_running_task ? ts->asyncio_running_task : Py_None); return -1; } Py_CLEAR(ts->asyncio_running_task); @@ -2328,7 +2328,7 @@ _asyncio_Task___init___impl(TaskObj *self, PyObject *coro, PyObject *loop, self->task_log_destroy_pending = 0; PyErr_Format(PyExc_TypeError, "a coroutine was expected, got %R", - coro, NULL); + coro); return -1; } diff --git a/Modules/_remote_debugging/asyncio.c b/Modules/_remote_debugging/asyncio.c index 12a8a9acc13bac..263c502a857004 100644 --- a/Modules/_remote_debugging/asyncio.c +++ b/Modules/_remote_debugging/asyncio.c @@ -212,7 +212,7 @@ parse_task_name( set_exception_cause(unwinder, PyExc_RuntimeError, "Task name PyLong parsing failed"); return NULL; } - return PyUnicode_FromFormat("Task-%d", res); + return PyUnicode_FromFormat("Task-%ld", res); } if(!(GET_MEMBER(unsigned long, type_obj, unwinder->debug_offsets.type_object.tp_flags) & Py_TPFLAGS_UNICODE_SUBCLASS)) { diff --git a/Modules/_ssl.c b/Modules/_ssl.c index d42a4e7f7078e6..b93bbe8ddf5a83 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -592,7 +592,7 @@ fill_and_set_sslerror(_sslmodulestate *state, } else { if (PyUnicodeWriter_Format( - writer, "unknown error (0x%x)", errcode) < 0) { + writer, "unknown error (0x%lx)", errcode) < 0) { goto fail; } } @@ -4016,15 +4016,11 @@ _ssl__SSLContext_verify_flags_set_impl(PySSLContext *self, PyObject *value) static int set_min_max_proto_version(PySSLContext *self, PyObject *arg, int what) { - long v; + int v; int result; - if (!PyArg_Parse(arg, "l", &v)) + if (!PyArg_Parse(arg, "i", &v)) return -1; - if (v > INT_MAX) { - PyErr_SetString(PyExc_OverflowError, "Option is too long"); - return -1; - } switch(self->protocol) { case PY_SSL_VERSION_TLS_CLIENT: _Py_FALLTHROUGH; @@ -4059,7 +4055,7 @@ set_min_max_proto_version(PySSLContext *self, PyObject *arg, int what) break; default: PyErr_Format(PyExc_ValueError, - "Unsupported TLS/SSL version 0x%x", v); + "Unsupported TLS/SSL version 0x%x", (unsigned)v); return -1; } @@ -4093,7 +4089,7 @@ set_min_max_proto_version(PySSLContext *self, PyObject *arg, int what) } if (result == 0) { PyErr_Format(PyExc_ValueError, - "Unsupported protocol version 0x%x", v); + "Unsupported protocol version 0x%x", (unsigned)v); return -1; } return 0; diff --git a/Modules/_zoneinfo.c b/Modules/_zoneinfo.c index 159cac3c06601a..aa0b1302cb2fc6 100644 --- a/Modules/_zoneinfo.c +++ b/Modules/_zoneinfo.c @@ -991,7 +991,7 @@ load_data(zoneinfo_state *state, PyZoneInfo_ZoneInfo *self, PyObject *file_obj) } if (!PyTuple_CheckExact(data_tuple)) { - PyErr_Format(PyExc_TypeError, "Invalid data result type: %r", + PyErr_Format(PyExc_TypeError, "Invalid data result type: %R", data_tuple); goto error; } diff --git a/Modules/binascii.c b/Modules/binascii.c index dbe77ff248d34e..c51bb9c3c77371 100644 --- a/Modules/binascii.c +++ b/Modules/binascii.c @@ -1350,7 +1350,7 @@ binascii_a2b_base85_impl(PyObject *module, Py_buffer *data, state = get_binascii_state(module); if (state != NULL) { PyErr_Format(state->Error, - "Base85 overflow in hunk starting at byte %d", + "Base85 overflow in hunk starting at byte %zd", (data->len - ascii_len) / 5 * 5); } goto error; @@ -1361,7 +1361,7 @@ binascii_a2b_base85_impl(PyObject *module, Py_buffer *data, else { state = get_binascii_state(module); if (state != NULL) { - PyErr_Format(state->Error, "bad Base85 character at position %d", + PyErr_Format(state->Error, "bad Base85 character at position %zd", data->len - ascii_len); } goto error; diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 1cedd64e0e9953..a97b09a4f5df0e 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -3357,8 +3357,7 @@ sock_setsockopt(PyObject *self, PyObject *args) arglen = PyTuple_Size(args); if (arglen == 3 && optval == Py_None) { PyErr_Format(PyExc_TypeError, - "setsockopt() requires 4 arguments when the third argument is None", - arglen); + "setsockopt() requires 4 arguments when the third argument is None"); return NULL; } if (arglen == 4 && optval != Py_None) { From b7055533abc2f7f93e04778fb70664096aa3d3b5 Mon Sep 17 00:00:00 2001 From: sunmy2019 <59365878+sunmy2019@users.noreply.github.com> Date: Tue, 31 Mar 2026 15:58:27 +0800 Subject: [PATCH 3/7] gh-146615: Fix format specifiers in test cextensions (GH-146618) --- Modules/_testcapi/watchers.c | 4 ++-- Modules/_testcapimodule.c | 4 ++-- Modules/_testinternalcapi.c | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Modules/_testcapi/watchers.c b/Modules/_testcapi/watchers.c index 6d061bb8d51040..5a756a87c15fe9 100644 --- a/Modules/_testcapi/watchers.c +++ b/Modules/_testcapi/watchers.c @@ -364,7 +364,7 @@ add_code_watcher(PyObject *self, PyObject *which_watcher) watcher_id = PyCode_AddWatcher(error_code_event_handler); } else { - PyErr_Format(PyExc_ValueError, "invalid watcher %d", which_l); + PyErr_Format(PyExc_ValueError, "invalid watcher %ld", which_l); return NULL; } if (watcher_id < 0) { @@ -673,7 +673,7 @@ add_context_watcher(PyObject *self, PyObject *which_watcher) assert(PyLong_Check(which_watcher)); long which_l = PyLong_AsLong(which_watcher); if (which_l < 0 || which_l >= (long)Py_ARRAY_LENGTH(callbacks)) { - PyErr_Format(PyExc_ValueError, "invalid watcher %d", which_l); + PyErr_Format(PyExc_ValueError, "invalid watcher %ld", which_l); return NULL; } int watcher_id = PyContext_AddWatcher(callbacks[which_l]); diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index a76af1416e091f..aa12db20908b97 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -116,8 +116,8 @@ test_sizeof_c_types(PyObject *self, PyObject *Py_UNUSED(ignored)) do { \ if (EXPECTED != sizeof(TYPE)) { \ PyErr_Format(get_testerror(self), \ - "sizeof(%s) = %u instead of %u", \ - #TYPE, sizeof(TYPE), EXPECTED); \ + "sizeof(%s) = %zu instead of %u", \ + #TYPE, sizeof(TYPE), (unsigned)(EXPECTED)); \ return (PyObject*)NULL; \ } \ } while (0) diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index 7f6ea621f87145..c00bad46a54907 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -417,14 +417,14 @@ test_bswap(PyObject *self, PyObject *Py_UNUSED(args)) uint16_t u16 = _Py_bswap16(UINT16_C(0x3412)); if (u16 != UINT16_C(0x1234)) { PyErr_Format(PyExc_AssertionError, - "_Py_bswap16(0x3412) returns %u", u16); + "_Py_bswap16(0x3412) returns %d", u16); return NULL; } uint32_t u32 = _Py_bswap32(UINT32_C(0x78563412)); if (u32 != UINT32_C(0x12345678)) { PyErr_Format(PyExc_AssertionError, - "_Py_bswap32(0x78563412) returns %lu", u32); + "_Py_bswap32(0x78563412) returns %u", u32); return NULL; } @@ -703,7 +703,7 @@ test_edit_cost(PyObject *self, PyObject *Py_UNUSED(args)) static int check_bytes_find(const char *haystack0, const char *needle0, - int offset, Py_ssize_t expected) + Py_ssize_t offset, Py_ssize_t expected) { Py_ssize_t len_haystack = strlen(haystack0); Py_ssize_t len_needle = strlen(needle0); @@ -1158,7 +1158,7 @@ get_interp_settings(PyObject *self, PyObject *args) } else { PyErr_Format(PyExc_NotImplementedError, - "%zd", interpid); + "%d", interpid); return NULL; } assert(interp != NULL); From dcb260eff2d276976933f78c24a4ebd0ed7dbc36 Mon Sep 17 00:00:00 2001 From: sunmy2019 <59365878+sunmy2019@users.noreply.github.com> Date: Tue, 31 Mar 2026 15:59:17 +0800 Subject: [PATCH 4/7] gh-146615: Fix format specifiers in Python/ directory (GH-146619) --- Python/bltinmodule.c | 8 ++++---- Python/ceval.c | 4 ++-- Python/crossinterp_data_lookup.h | 2 +- Python/getargs.c | 6 +++--- Python/interpconfig.c | 2 +- Python/pythonrun.c | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 5680e8971576cd..fec64e1ff9d25f 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -1607,7 +1607,7 @@ map_next(PyObject *self) // ValueError: map() argument 3 is shorter than arguments 1-2 const char* plural = i == 1 ? " " : "s 1-"; PyErr_Format(PyExc_ValueError, - "map() argument %d is shorter than argument%s%d", + "map() argument %zd is shorter than argument%s%zd", i + 1, plural, i); goto exit_no_result; } @@ -1618,7 +1618,7 @@ map_next(PyObject *self) Py_DECREF(val); const char* plural = i == 1 ? " " : "s 1-"; PyErr_Format(PyExc_ValueError, - "map() argument %d is longer than argument%s%d", + "map() argument %zd is longer than argument%s%zd", i + 1, plural, i); goto exit_no_result; } @@ -3307,7 +3307,7 @@ zip_next(PyObject *self) // ValueError: zip() argument 3 is shorter than arguments 1-2 const char* plural = i == 1 ? " " : "s 1-"; return PyErr_Format(PyExc_ValueError, - "zip() argument %d is shorter than argument%s%d", + "zip() argument %zd is shorter than argument%s%zd", i + 1, plural, i); } for (i = 1; i < tuplesize; i++) { @@ -3317,7 +3317,7 @@ zip_next(PyObject *self) Py_DECREF(item); const char* plural = i == 1 ? " " : "s 1-"; return PyErr_Format(PyExc_ValueError, - "zip() argument %d is longer than argument%s%d", + "zip() argument %zd is longer than argument%s%zd", i + 1, plural, i); } if (PyErr_Occurred()) { diff --git a/Python/ceval.c b/Python/ceval.c index 49aeffc3caf849..bf550f2da3662e 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -602,7 +602,7 @@ _PyEval_MatchClass(PyThreadState *tstate, PyObject *subject, PyObject *type, if (allowed < nargs) { const char *plural = (allowed == 1) ? "" : "s"; _PyErr_Format(tstate, PyExc_TypeError, - "%s() accepts %d positional sub-pattern%s (%d given)", + "%s() accepts %zd positional sub-pattern%s (%zd given)", ((PyTypeObject*)type)->tp_name, allowed, plural, nargs); goto fail; @@ -1555,7 +1555,7 @@ format_missing(PyThreadState *tstate, const char *kind, if (name_str == NULL) return; _PyErr_Format(tstate, PyExc_TypeError, - "%U() missing %i required %s argument%s: %U", + "%U() missing %zd required %s argument%s: %U", qualname, len, kind, diff --git a/Python/crossinterp_data_lookup.h b/Python/crossinterp_data_lookup.h index c3c76ae8d9a289..cf84633e10e356 100644 --- a/Python/crossinterp_data_lookup.h +++ b/Python/crossinterp_data_lookup.h @@ -455,7 +455,7 @@ _PyBytes_GetXIDataWrapped(PyThreadState *tstate, return NULL; } if (size < sizeof(_PyBytes_data_t)) { - PyErr_Format(PyExc_ValueError, "expected size >= %d, got %d", + PyErr_Format(PyExc_ValueError, "expected size >= %zu, got %zu", sizeof(_PyBytes_data_t), size); return NULL; } diff --git a/Python/getargs.c b/Python/getargs.c index 31cd4ad3f652d9..3f423266bff7f4 100644 --- a/Python/getargs.c +++ b/Python/getargs.c @@ -2421,7 +2421,7 @@ vgetargskeywordsfast_impl(PyObject *const *args, Py_ssize_t nargs, if (i < parser->min) { /* Less arguments than required */ if (i < pos) { - Py_ssize_t min = Py_MIN(pos, parser->min); + int min = Py_MIN(pos, parser->min); PyErr_Format(PyExc_TypeError, "%.200s%s takes %s %d positional argument%s" " (%zd given)", @@ -2435,7 +2435,7 @@ vgetargskeywordsfast_impl(PyObject *const *args, Py_ssize_t nargs, else { keyword = PyTuple_GET_ITEM(kwtuple, i - pos); PyErr_Format(PyExc_TypeError, "%.200s%s missing required " - "argument '%U' (pos %d)", + "argument '%U' (pos %zd)", (parser->fname == NULL) ? "function" : parser->fname, (parser->fname == NULL) ? "" : "()", keyword, i+1); @@ -2476,7 +2476,7 @@ vgetargskeywordsfast_impl(PyObject *const *args, Py_ssize_t nargs, /* arg present in tuple and in dict */ PyErr_Format(PyExc_TypeError, "argument for %.200s%s given by name ('%U') " - "and position (%d)", + "and position (%zd)", (parser->fname == NULL) ? "function" : parser->fname, (parser->fname == NULL) ? "" : "()", keyword, i+1); diff --git a/Python/interpconfig.c b/Python/interpconfig.c index 1add8a81425b9a..a37bd3f5b23a01 100644 --- a/Python/interpconfig.c +++ b/Python/interpconfig.c @@ -208,7 +208,7 @@ interp_config_from_dict(PyObject *origdict, PyInterpreterConfig *config, } else if (unused > 0) { PyErr_Format(PyExc_ValueError, - "config dict has %d extra items (%R)", unused, dict); + "config dict has %zd extra items (%R)", unused, dict); goto error; } diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 1e327848e656af..971ab064777a41 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -1382,11 +1382,11 @@ get_interactive_filename(PyObject *filename, Py_ssize_t count) if (middle == NULL) { return NULL; } - result = PyUnicode_FromFormat("<%U-%d>", middle, count); + result = PyUnicode_FromFormat("<%U-%zd>", middle, count); Py_DECREF(middle); } else { result = PyUnicode_FromFormat( - "%U-%d", filename, count); + "%U-%zd", filename, count); } return result; From bbf7fb2c15a1dc9a54d10937c3d0831b0968257d Mon Sep 17 00:00:00 2001 From: sunmy2019 <59365878+sunmy2019@users.noreply.github.com> Date: Tue, 31 Mar 2026 15:59:48 +0800 Subject: [PATCH 5/7] gh-146615: Fix format specifiers in Objects/ directory (GH-146620) --- Objects/descrobject.c | 2 +- Objects/enumobject.c | 2 +- Objects/exceptions.c | 4 ++-- Objects/funcobject.c | 4 ++-- Objects/memoryobject.c | 2 +- Objects/typeobject.c | 8 ++++---- Objects/typevarobject.c | 2 +- Objects/unicodeobject.c | 6 +++--- Objects/unionobject.c | 2 +- 9 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Objects/descrobject.c b/Objects/descrobject.c index 517d9e9fa9d45d..a5926616eeb3cb 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -1610,7 +1610,7 @@ property_set_name(PyObject *self, PyObject *args) { if (PyTuple_GET_SIZE(args) != 2) { PyErr_Format( PyExc_TypeError, - "__set_name__() takes 2 positional arguments but %d were given", + "__set_name__() takes 2 positional arguments but %zd were given", PyTuple_GET_SIZE(args)); return NULL; } diff --git a/Objects/enumobject.c b/Objects/enumobject.c index 597b4c94dbf0d3..364d508dd01822 100644 --- a/Objects/enumobject.c +++ b/Objects/enumobject.c @@ -148,7 +148,7 @@ enumerate_vectorcall(PyObject *type, PyObject *const *args, } PyErr_Format(PyExc_TypeError, - "enumerate() takes at most 2 arguments (%d given)", nargs + nkwargs); + "enumerate() takes at most 2 arguments (%zd given)", nargs + nkwargs); return NULL; } diff --git a/Objects/exceptions.c b/Objects/exceptions.c index c4a591e2cf7f8c..5e5e87cd6d7559 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -935,7 +935,7 @@ BaseExceptionGroup_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (!PyExceptionInstance_Check(exc)) { PyErr_Format( PyExc_ValueError, - "Item %d of second argument (exceptions) is not an exception", + "Item %zd of second argument (exceptions) is not an exception", i); goto error; } @@ -1714,7 +1714,7 @@ PyUnstable_Exc_PrepReraiseStar(PyObject *orig, PyObject *excs) PyObject *exc = PyList_GET_ITEM(excs, i); if (exc == NULL || !(PyExceptionInstance_Check(exc) || Py_IsNone(exc))) { PyErr_Format(PyExc_TypeError, - "item %d of excs is not an exception", i); + "item %zd of excs is not an exception", i); return NULL; } } diff --git a/Objects/funcobject.c b/Objects/funcobject.c index 585c7b9a85412c..d47c78b933b702 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -657,7 +657,7 @@ func_set_code(PyObject *self, PyObject *value, void *Py_UNUSED(ignored)) if (nclosure != nfree) { PyErr_Format(PyExc_ValueError, "%U() requires a code object with %zd free vars," - " not %zd", + " not %d", op->func_name, nclosure, nfree); return -1; @@ -1044,7 +1044,7 @@ func_new_impl(PyTypeObject *type, PyCodeObject *code, PyObject *globals, nclosure = closure == Py_None ? 0 : PyTuple_GET_SIZE(closure); if (code->co_nfreevars != nclosure) return PyErr_Format(PyExc_ValueError, - "%U requires closure of length %zd, not %zd", + "%U requires closure of length %d, not %zd", code->co_name, code->co_nfreevars, nclosure); if (nclosure) { Py_ssize_t i; diff --git a/Objects/memoryobject.c b/Objects/memoryobject.c index 00e7955d15118a..bca77851ac2961 100644 --- a/Objects/memoryobject.c +++ b/Objects/memoryobject.c @@ -2472,7 +2472,7 @@ ptr_from_tuple(const Py_buffer *view, PyObject *tup) if (nindices > view->ndim) { PyErr_Format(PyExc_TypeError, - "cannot index %zd-dimension view with %zd-element tuple", + "cannot index %d-dimension view with %zd-element tuple", view->ndim, nindices); return NULL; } diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 6ceeb7dda08e9f..b19aee6338dcc0 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -5184,28 +5184,28 @@ check_basicsize_includes_size_and_offsets(PyTypeObject* type) if (type->tp_base && type->tp_base->tp_basicsize > type->tp_basicsize) { PyErr_Format(PyExc_TypeError, - "tp_basicsize for type '%s' (%d) is too small for base '%s' (%d)", + "tp_basicsize for type '%s' (%zd) is too small for base '%s' (%zd)", type->tp_name, type->tp_basicsize, type->tp_base->tp_name, type->tp_base->tp_basicsize); return 0; } if (type->tp_weaklistoffset + (Py_ssize_t)sizeof(PyObject*) > max) { PyErr_Format(PyExc_TypeError, - "weaklist offset %d is out of bounds for type '%s' (tp_basicsize = %d)", + "weaklist offset %zd is out of bounds for type '%s' (tp_basicsize = %zd)", type->tp_weaklistoffset, type->tp_name, type->tp_basicsize); return 0; } if (type->tp_dictoffset + (Py_ssize_t)sizeof(PyObject*) > max) { PyErr_Format(PyExc_TypeError, - "dict offset %d is out of bounds for type '%s' (tp_basicsize = %d)", + "dict offset %zd is out of bounds for type '%s' (tp_basicsize = %zd)", type->tp_dictoffset, type->tp_name, type->tp_basicsize); return 0; } if (type->tp_vectorcall_offset + (Py_ssize_t)sizeof(vectorcallfunc*) > max) { PyErr_Format(PyExc_TypeError, - "vectorcall offset %d is out of bounds for type '%s' (tp_basicsize = %d)", + "vectorcall offset %zd is out of bounds for type '%s' (tp_basicsize = %zd)", type->tp_vectorcall_offset, type->tp_name, type->tp_basicsize); return 0; diff --git a/Objects/typevarobject.c b/Objects/typevarobject.c index a206bd7b5dd404..b5413ee37a9358 100644 --- a/Objects/typevarobject.c +++ b/Objects/typevarobject.c @@ -818,7 +818,7 @@ typevar_typing_prepare_subst_impl(typevarobject *self, PyObject *alias, } Py_DECREF(params); PyErr_Format(PyExc_TypeError, - "Too few arguments for %S; actual %d, expected at least %d", + "Too few arguments for %S; actual %zd, expected at least %zd", alias, args_len, i + 1); return NULL; } diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index daf4651c4313b3..35bd88d6254d9c 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -8350,7 +8350,7 @@ charmap_decode_mapping(const char *s, goto Undefined; if (value < 0 || value > MAX_UNICODE) { PyErr_Format(PyExc_TypeError, - "character mapping must be in range(0x%x)", + "character mapping must be in range(0x%lx)", (unsigned long)MAX_UNICODE + 1); goto onError; } @@ -9141,8 +9141,8 @@ charmaptranslate_lookup(Py_UCS4 c, PyObject *mapping, PyObject **result, Py_UCS4 long value = PyLong_AsLong(x); if (value < 0 || value > MAX_UNICODE) { PyErr_Format(PyExc_ValueError, - "character mapping must be in range(0x%x)", - MAX_UNICODE+1); + "character mapping must be in range(0x%lx)", + (unsigned long)MAX_UNICODE + 1); Py_DECREF(x); return -1; } diff --git a/Objects/unionobject.c b/Objects/unionobject.c index a47d6193d70889..d33d581f049c5b 100644 --- a/Objects/unionobject.c +++ b/Objects/unionobject.c @@ -61,7 +61,7 @@ union_hash(PyObject *self) } // The unhashable values somehow became hashable again. Still raise // an error. - PyErr_Format(PyExc_TypeError, "union contains %d unhashable elements", n); + PyErr_Format(PyExc_TypeError, "union contains %zd unhashable elements", n); return -1; } return PyObject_Hash(alias->hashable_args); From b4fac15613a16f9cd7b2ee32840523b399f4621f Mon Sep 17 00:00:00 2001 From: Thomas Kowalski Date: Tue, 31 Mar 2026 10:57:07 +0200 Subject: [PATCH 6/7] gh-145458: use `self.skip_idle` consistently in the tachyon profiler (#145459) --- Lib/profiling/sampling/stack_collector.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/profiling/sampling/stack_collector.py b/Lib/profiling/sampling/stack_collector.py index 05ebe4d9764758..31102d3eb0ffa6 100644 --- a/Lib/profiling/sampling/stack_collector.py +++ b/Lib/profiling/sampling/stack_collector.py @@ -19,9 +19,9 @@ def __init__(self, sample_interval_usec, *, skip_idle=False): self.sample_interval_usec = sample_interval_usec self.skip_idle = skip_idle - def collect(self, stack_frames, timestamps_us=None, skip_idle=False): + def collect(self, stack_frames, timestamps_us=None): weight = len(timestamps_us) if timestamps_us else 1 - for frames, thread_id in self._iter_stacks(stack_frames, skip_idle=skip_idle): + for frames, thread_id in self._iter_stacks(stack_frames, skip_idle=self.skip_idle): self.process_frames(frames, thread_id, weight=weight) def process_frames(self, frames, thread_id, weight=1): @@ -88,7 +88,7 @@ def __init__(self, *args, **kwargs): # Per-thread statistics self.per_thread_stats = {} # {thread_id: {has_gil, on_cpu, gil_requested, unknown, has_exception, total, gc_samples}} - def collect(self, stack_frames, timestamps_us=None, skip_idle=False): + def collect(self, stack_frames, timestamps_us=None): """Override to track thread status statistics before processing frames.""" # Weight is number of timestamps (samples with identical stack) weight = len(timestamps_us) if timestamps_us else 1 @@ -123,7 +123,7 @@ def collect(self, stack_frames, timestamps_us=None, skip_idle=False): self.per_thread_stats[thread_id][key] += value * weight # Call parent collect to process frames - super().collect(stack_frames, timestamps_us, skip_idle=skip_idle) + super().collect(stack_frames, timestamps_us) def set_stats(self, sample_interval_usec, duration_sec, sample_rate, error_rate=None, missed_samples=None, mode=None): From 67354b2925e28b3bcc6e5b52bf92cd5f4cc69d3c Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Tue, 31 Mar 2026 13:02:35 +0200 Subject: [PATCH 7/7] gh-145563: Add thread-safety annotation for PyCapsule C-API (#146612) --- Doc/data/threadsafety.dat | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/Doc/data/threadsafety.dat b/Doc/data/threadsafety.dat index afb053adf5c62b..82edd1167ef128 100644 --- a/Doc/data/threadsafety.dat +++ b/Doc/data/threadsafety.dat @@ -123,4 +123,33 @@ PyByteArray_GET_SIZE:atomic: # Raw data - no locking; mutating it is unsafe if the bytearray object is shared between threads PyByteArray_AsString:compatible: -PyByteArray_AS_STRING:compatible: \ No newline at end of file +PyByteArray_AS_STRING:compatible: + +# Capsule objects (Doc/c-api/capsule.rst) + +# Type check - read ob_type pointer, always safe +PyCapsule_CheckExact:atomic: + +# Creation - pure allocation, no shared state +PyCapsule_New:atomic: + +# Validation - reads pointer and name fields; safe on distinct objects +PyCapsule_IsValid:distinct: + +# Getters - read struct fields; safe on distinct objects but +# concurrent access to the same capsule requires external synchronization +PyCapsule_GetPointer:distinct: +PyCapsule_GetName:distinct: +PyCapsule_GetDestructor:distinct: +PyCapsule_GetContext:distinct: + +# Setters - write struct fields; safe on distinct objects but +# concurrent access to the same capsule requires external synchronization +PyCapsule_SetPointer:distinct: +PyCapsule_SetName:distinct: +PyCapsule_SetDestructor:distinct: +PyCapsule_SetContext:distinct: + +# Import - looks up a capsule from a module attribute and +# calls PyCapsule_GetPointer; may call arbitrary code +PyCapsule_Import:compatible: