diff --git a/Misc/NEWS.d/next/C_API/2026-09-16-21-24-36.gh-issue-142217.44sqt4.rst b/Misc/NEWS.d/next/C_API/2026-09-16-21-24-36.gh-issue-142217.44sqt4.rst new file mode 100644 index 000000000000000..7d48e27d8a83409 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2026-09-16-21-24-36.gh-issue-142217.44sqt4.rst @@ -0,0 +1,2 @@ +Add tests on the undocumented and deprecated ``_Py_Identifier`` C API. Patch +by Victor Stinner. diff --git a/Modules/_pickle.c b/Modules/_pickle.c index 90200a4379319d9..5339337741d2f27 100644 --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -4745,17 +4745,9 @@ save(PickleState *st, PicklerObject *self, PyObject *obj, int pers_save) * __reduce_ex__ method, or the object's __reduce__ method. */ if (self->dispatch_table == NULL) { - reduce_func = PyDict_GetItemWithError(st->dispatch_table, - (PyObject *)type); - if (reduce_func == NULL) { - if (PyErr_Occurred()) { - goto error; - } - } else { - /* PyDict_GetItemWithError() returns a borrowed reference. - Increase the reference count to be consistent with - PyObject_GetItem and _PyObject_GetAttrId used below. */ - Py_INCREF(reduce_func); + if (PyDict_GetItemRef(st->dispatch_table, (PyObject *)type, + &reduce_func) < 0) { + goto error; } } else if (PyMapping_GetOptionalItem(self->dispatch_table, (PyObject *)type, diff --git a/Modules/_testcapi/unicode.c b/Modules/_testcapi/unicode.c index 915c9230f66b52e..e4017efd19237fd 100644 --- a/Modules/_testcapi/unicode.c +++ b/Modules/_testcapi/unicode.c @@ -227,6 +227,55 @@ unicode_GET_CACHED_HASH(PyObject *self, PyObject *arg) } +// Test the deprecated _Py_Identifier C API: +// - _Py_IDENTIFIER() +// - _Py_static_string() +// - _Py_static_string_init() +// - _PyObject_CallMethodId() +// - _PyObject_GetAttrId() +// - _PyUnicode_FromId() +// +// _testembed also has tests on _PyUnicode_FromId(). +static PyObject* +test_py_identifier(PyObject *self, PyObject *Py_UNUSED(args)) +{ +// Ignore deprecation warnings +_Py_COMP_DIAG_PUSH +_Py_COMP_DIAG_IGNORE_DEPR_DECLS + + _Py_IDENTIFIER(hello); + PyObject *str = _PyUnicode_FromId(&PyId_hello); // borrowed ref + if (str == NULL) { + return NULL; + } + assert(PyUnicode_EqualToUTF8(str, "hello") == 1); + + // Calling twice return the same object + PyObject *str2 = _PyUnicode_FromId(&PyId_hello); // borrowed ref + assert(str2 == str); + + PyObject *number = Py_GetConstant(Py_CONSTANT_ONE); // immortal + _Py_static_string(to_bytes_id, "to_bytes"); + PyObject *res = _PyObject_CallMethodId(number, &to_bytes_id, NULL); + if (res == NULL) { + return NULL; + } + Py_DECREF(res); + + static _Py_Identifier real_id = _Py_static_string_init("real"); + res = _PyObject_GetAttrId(number, &real_id); + if (res == NULL) { + return NULL; + } + assert(res == number); + Py_DECREF(res); + + Py_RETURN_NONE; + +_Py_COMP_DIAG_POP +} + + // --- PyUnicodeWriter type ------------------------------------------------- typedef struct { @@ -572,6 +621,7 @@ static PyMethodDef TestMethods[] = { {"unicode_asutf8", unicode_asutf8, METH_VARARGS}, {"unicode_copycharacters", unicode_copycharacters, METH_VARARGS}, {"unicode_GET_CACHED_HASH", unicode_GET_CACHED_HASH, METH_O}, + {"test_py_identifier", test_py_identifier, METH_NOARGS}, {NULL}, }; diff --git a/Tools/c-analyzer/cpython/ignored.tsv b/Tools/c-analyzer/cpython/ignored.tsv index 4c143164650a2ba..cddfeb02c4b8597 100644 --- a/Tools/c-analyzer/cpython/ignored.tsv +++ b/Tools/c-analyzer/cpython/ignored.tsv @@ -787,7 +787,9 @@ Modules/clinic/grpmodule.c.h grp_getgrgid _keywords - Modules/clinic/grpmodule.c.h grp_getgrnam _keywords - Objects/object.c - constants static PyObject*[] Objects/dictobject.c - PyFrozenDict_Type - - +Modules/_testcapi/unicode.c test_py_identifier PyId_hello - +Modules/_testcapi/unicode.c test_py_identifier to_bytes_id - +Modules/_testcapi/unicode.c test_py_identifier real_id - ## False positives Python/specialize.c - _Py_InitCleanup -