diff --git a/Doc/c-api/module.rst b/Doc/c-api/module.rst index 3ea7aeb55d0..8a15a5ea83e 100644 --- a/Doc/c-api/module.rst +++ b/Doc/c-api/module.rst @@ -549,6 +549,14 @@ state: Note that ``Py_XDECREF()`` should be used instead of ``Py_DECREF()`` in this case, since *obj* can be ``NULL``. + The number of different *name* strings passed to this function + should be kept small, usually by only using statically allocated strings + as *name*. + For names that aren't known at compile time, prefer calling + :c:func:`PyUnicode_FromString` and :c:func:`PyObject_SetAttr` directly. + For more details, see :c:func:`PyUnicode_InternFromString`, which may be + used internally to create a key object. + .. versionadded:: 3.10 @@ -610,6 +618,9 @@ state: used from the module's initialization function. Return ``-1`` with an exception set on error, ``0`` on success. + This is a convenience function that calls :c:func:`PyLong_FromLong` and + :c:func:`PyModule_AddObjectRef`; see their documentation for details. + .. c:function:: int PyModule_AddStringConstant(PyObject *module, const char *name, const char *value) @@ -618,6 +629,10 @@ state: ``NULL``-terminated. Return ``-1`` with an exception set on error, ``0`` on success. + This is a convenience function that calls + :c:func:`PyUnicode_InternFromString` and :c:func:`PyModule_AddObjectRef`; + see their documentation for details. + .. c:macro:: PyModule_AddIntMacro(module, macro) diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index 2103a64d8ff..1c28f30321b 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -206,6 +206,13 @@ Object Protocol If *v* is ``NULL``, the attribute is deleted, but this feature is deprecated in favour of using :c:func:`PyObject_DelAttrString`. + The number of different attribute names passed to this function + should be kept small, usually by using a statically allocated string + as *attr_name*. + For attribute names that aren't known at compile time, prefer calling + :c:func:`PyUnicode_FromString` and :c:func:`PyObject_SetAttr` directly. + For more details, see :c:func:`PyUnicode_InternFromString`, which may be + used internally to create a key object. .. c:function:: int PyObject_GenericSetAttr(PyObject *o, PyObject *name, PyObject *value) @@ -231,6 +238,14 @@ Object Protocol specified as a :c:expr:`const char*` UTF-8 encoded bytes string, rather than a :c:expr:`PyObject*`. + The number of different attribute names passed to this function + should be kept small, usually by using a statically allocated string + as *attr_name*. + For attribute names that aren't known at compile time, prefer calling + :c:func:`PyUnicode_FromString` and :c:func:`PyObject_DelAttr` directly. + For more details, see :c:func:`PyUnicode_InternFromString`, which may be + used internally to create a key object for lookup. + .. c:function:: PyObject* PyObject_GenericGetDict(PyObject *o, void *context) diff --git a/Doc/c-api/unicode.rst b/Doc/c-api/unicode.rst index 246cf47df62..958fafd47ac 100644 --- a/Doc/c-api/unicode.rst +++ b/Doc/c-api/unicode.rst @@ -1490,18 +1490,43 @@ They all return ``NULL`` or ``-1`` if an exception occurs. existing interned string that is the same as :c:expr:`*p_unicode`, it sets :c:expr:`*p_unicode` to it (releasing the reference to the old string object and creating a new :term:`strong reference` to the interned string object), otherwise it leaves - :c:expr:`*p_unicode` alone and interns it (creating a new :term:`strong reference`). + :c:expr:`*p_unicode` alone and interns it. + (Clarification: even though there is a lot of talk about references, think - of this function as reference-neutral; you own the object after the call - if and only if you owned it before the call.) + of this function as reference-neutral. You must own the object you pass in; + after the call you no longer own the passed-in reference, but you newly own + the result.) + + This function never raises an exception. + On error, it leaves its argument unchanged without interning it. + + Instances of subclasses of :py:class:`str` may not be interned, that is, + :c:expr:`PyUnicode_CheckExact(*p_unicode)` must be true. If it is not, + then -- as with any other error -- the argument is left unchanged. + + Note that interned strings are not “immortal”. + You must keep a reference to the result to benefit from interning. .. c:function:: PyObject* PyUnicode_InternFromString(const char *str) A combination of :c:func:`PyUnicode_FromString` and - :c:func:`PyUnicode_InternInPlace`, returning either a new Unicode string - object that has been interned, or a new ("owned") reference to an earlier - interned string object with the same value. + :c:func:`PyUnicode_InternInPlace`, meant for statically allocated strings. + + Return a new ("owned") reference to either a new Unicode string object + that has been interned, or an earlier interned string object with the + same value. + + Python may keep a reference to the result, or make it :term:`immortal`, + preventing it from being garbage-collected promptly. + For interning an unbounded number of different strings, such as ones coming + from user input, prefer calling :c:func:`PyUnicode_FromString` and + :c:func:`PyUnicode_InternInPlace` directly. + + .. impl-detail:: + + Strings interned this way are made :term:`immortal`. + PyUnicodeWriter ^^^^^^^^^^^^^^^ diff --git a/Misc/NEWS.d/next/C API/2024-07-04-15-41-10.gh-issue-113993.cLSiWV.rst b/Misc/NEWS.d/next/C API/2024-07-04-15-41-10.gh-issue-113993.cLSiWV.rst new file mode 100644 index 00000000000..9b7f2082065 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2024-07-04-15-41-10.gh-issue-113993.cLSiWV.rst @@ -0,0 +1,12 @@ +:c:func:`PyUnicode_InternInPlace` no longer prevents its argument from being +garbage collected. + +Several functions that take ``char *`` are now +documented as possibly preventing string objects from being garbage +collected; refer to their documentation for details: +:c:func:`PyUnicode_InternFromString`, +:c:func:`PyDict_SetItemString`, +:c:func:`PyObject_SetAttrString`, +:c:func:`PyObject_DelAttrString`, +:c:func:`PyUnicode_InternFromString`, +and ``PyModule_Add*`` convenience functions. diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index db58f33511c..b55102639c6 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -2303,9 +2303,14 @@ PyCSimpleType_init(PyObject *self, PyObject *args, PyObject *kwds) if (!meth) { return -1; } - x = PyDict_SetItemString(((PyTypeObject*)self)->tp_dict, - ml->ml_name, - meth); + PyObject *name = PyUnicode_FromString(ml->ml_name); + if (name == NULL) { + Py_DECREF(meth); + return -1; + } + PyUnicode_InternInPlace(&name); + x = PyDict_SetItem(((PyTypeObject*)self)->tp_dict, name, meth); + Py_DECREF(name); Py_DECREF(meth); if (x == -1) { return -1; diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index c2fb135ed68..408d74fb3af 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -15584,7 +15584,7 @@ void PyUnicode_InternInPlace(PyObject **p) { PyInterpreterState *interp = _PyInterpreterState_GET(); - _PyUnicode_InternImmortal(interp, p); + _PyUnicode_InternMortal(interp, p); } // Public-looking name kept for the stable ABI; user should not call this: