]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-42327: C API: Add PyModule_Add() function (GH-23443)
authorSerhiy Storchaka <storchaka@gmail.com>
Tue, 18 Jul 2023 06:42:05 +0000 (09:42 +0300)
committerGitHub <noreply@github.com>
Tue, 18 Jul 2023 06:42:05 +0000 (09:42 +0300)
It is a fixed implementation of PyModule_AddObject() which consistently
steals reference both on success and on failure.

Doc/c-api/module.rst
Doc/data/stable_abi.dat
Doc/whatsnew/3.13.rst
Include/modsupport.h
Lib/test/test_stable_abi_ctypes.py
Misc/NEWS.d/next/C API/2020-11-11-22-36-29.bpo-42327.ODSZBM.rst [new file with mode: 0644]
Misc/stable_abi.toml
PC/python3dll.c
Python/modsupport.c

index 230b471d473be71e86851fa50884de387bec4e4f..d35b302fce6aa6d92f5fc794569f04d4e021b595 100644 (file)
@@ -486,12 +486,29 @@ state:
    .. versionadded:: 3.10
 
 
+.. c:function:: int PyModule_Add(PyObject *module, const char *name, PyObject *value)
+
+   Similar to :c:func:`PyModule_AddObjectRef`, but "steals" a reference
+   to *value*.
+   It can be called with a result of function that returns a new reference
+   without bothering to check its result or even saving it to a variable.
+
+   Example usage::
+
+        if (PyModule_Add(module, "spam", PyBytes_FromString(value)) < 0) {
+            goto error;
+        }
+
+   .. versionadded:: 3.13
+
+
 .. c:function:: int PyModule_AddObject(PyObject *module, const char *name, PyObject *value)
 
    Similar to :c:func:`PyModule_AddObjectRef`, but steals a reference to
    *value* on success (if it returns ``0``).
 
-   The new :c:func:`PyModule_AddObjectRef` function is recommended, since it is
+   The new :c:func:`PyModule_Add` or :c:func:`PyModule_AddObjectRef`
+   functions are recommended, since it is
    easy to introduce reference leaks by misusing the
    :c:func:`PyModule_AddObject` function.
 
@@ -501,44 +518,24 @@ state:
       only decrements the reference count of *value* **on success**.
 
       This means that its return value must be checked, and calling code must
-      :c:func:`Py_DECREF` *value* manually on error.
+      :c:func:`Py_XDECREF` *value* manually on error.
 
    Example usage::
 
-      static int
-      add_spam(PyObject *module, int value)
-      {
-          PyObject *obj = PyLong_FromLong(value);
-          if (obj == NULL) {
-              return -1;
-          }
-          if (PyModule_AddObject(module, "spam", obj) < 0) {
-              Py_DECREF(obj);
-              return -1;
-          }
-          // PyModule_AddObject() stole a reference to obj:
-          // Py_DECREF(obj) is not needed here
-          return 0;
-      }
-
-   The example can also be written without checking explicitly if *obj* is
-   ``NULL``::
+        PyObject *obj = PyBytes_FromString(value);
+        if (PyModule_AddObject(module, "spam", obj) < 0) {
+            // If 'obj' is not NULL and PyModule_AddObject() failed,
+            // 'obj' strong reference must be deleted with Py_XDECREF().
+            // If 'obj' is NULL, Py_XDECREF() does nothing.
+            Py_XDECREF(obj);
+            goto error;
+        }
+        // PyModule_AddObject() stole a reference to obj:
+        // Py_XDECREF(obj) is not needed here.
 
-      static int
-      add_spam(PyObject *module, int value)
-      {
-          PyObject *obj = PyLong_FromLong(value);
-          if (PyModule_AddObject(module, "spam", obj) < 0) {
-              Py_XDECREF(obj);
-              return -1;
-          }
-          // PyModule_AddObject() stole a reference to obj:
-          // Py_DECREF(obj) is not needed here
-          return 0;
-      }
+   .. deprecated:: 3.13
 
-   Note that ``Py_XDECREF()`` should be used instead of ``Py_DECREF()`` in
-   this case, since *obj* can be ``NULL``.
+      :c:func:`PyModule_AddObject` is :term:`soft deprecated`.
 
 
 .. c:function:: int PyModule_AddIntConstant(PyObject *module, const char *name, long value)
index e3dd3dab27a035818832a2d11476bd57d43d1f55..aa1edf546370586b5e5a35a312145d4691d05898 100644 (file)
@@ -399,6 +399,7 @@ type,PyModuleDef,3.2,,full-abi
 type,PyModuleDef_Base,3.2,,full-abi
 function,PyModuleDef_Init,3.5,,
 var,PyModuleDef_Type,3.5,,
+function,PyModule_Add,3.13,,
 function,PyModule_AddFunctions,3.7,,
 function,PyModule_AddIntConstant,3.2,,
 function,PyModule_AddObject,3.2,,
index 161d5fb1c59a303293c88bb6cfdd9d1856963a30..0181e16f7d9b9d784be16c7798cfc4213e08f350 100644 (file)
@@ -774,6 +774,11 @@ New Features
   If the assertion fails, make sure that the size is set before.
   (Contributed by Victor Stinner in :gh:`106168`.)
 
+* Add :c:func:`PyModule_Add` function: similar to
+  :c:func:`PyModule_AddObjectRef` and :c:func:`PyModule_AddObject` but
+  always steals a reference to the value.
+  (Contributed by Serhiy Storchaka in :gh:`86493`.)
+
 Porting to Python 3.13
 ----------------------
 
index 7d4cfe853aaa7eca3a596a29e10a903d7e5eb7e5..51061c5bc8090a6a4e4770bdbf198a5b49cdcda6 100644 (file)
@@ -23,12 +23,18 @@ PyAPI_FUNC(PyObject *) Py_BuildValue(const char *, ...);
 PyAPI_FUNC(PyObject *) Py_VaBuildValue(const char *, va_list);
 
 // Add an attribute with name 'name' and value 'obj' to the module 'mod.
-// On success, return 0 on success.
+// On success, return 0.
 // On error, raise an exception and return -1.
 PyAPI_FUNC(int) PyModule_AddObjectRef(PyObject *mod, const char *name, PyObject *value);
 
-// Similar to PyModule_AddObjectRef() but steal a reference to 'obj'
-// (Py_DECREF(obj)) on success (if it returns 0).
+#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030d0000
+// Similar to PyModule_AddObjectRef() but steal a reference to 'value'.
+PyAPI_FUNC(int) PyModule_Add(PyObject *mod, const char *name, PyObject *value);
+#endif   /* Py_LIMITED_API */
+
+// Similar to PyModule_AddObjectRef() and PyModule_Add() but steal
+// a reference to 'value' on success and only on success.
+// Errorprone. Should not be used in new code.
 PyAPI_FUNC(int) PyModule_AddObject(PyObject *mod, const char *, PyObject *value);
 
 PyAPI_FUNC(int) PyModule_AddIntConstant(PyObject *, const char *, long);
index e92e986b293377af2a1a946dead786c3b588d7d8..4e74bb374c93bf4f3a9c658c9fd5f5fb97b1ab96 100644 (file)
@@ -425,6 +425,7 @@ SYMBOL_NAMES = (
     "PyMethodDescr_Type",
     "PyModuleDef_Init",
     "PyModuleDef_Type",
+    "PyModule_Add",
     "PyModule_AddFunctions",
     "PyModule_AddIntConstant",
     "PyModule_AddObject",
diff --git a/Misc/NEWS.d/next/C API/2020-11-11-22-36-29.bpo-42327.ODSZBM.rst b/Misc/NEWS.d/next/C API/2020-11-11-22-36-29.bpo-42327.ODSZBM.rst
new file mode 100644 (file)
index 0000000..3d935ac
--- /dev/null
@@ -0,0 +1 @@
+Add :func:`PyModule_Add` function: similar to :c:func:`PyModule_AddObjectRef` and :c:func:`PyModule_AddObject`, but always steals a reference to the value.
index 8ea8fde68b833a18251ad3c61439815c4f6e6bb8..dd2c9910b83ccbcab2f329618b1cf0cc8b6cb7d1 100644 (file)
     added = '3.13'
 [function.PyMapping_GetOptionalItemString]
     added = '3.13'
+[function.PyModule_Add]
+    added = '3.13'
index 8f2df6950cfc052c5102287afb3b5c9bb587bf88..0b54c5a707231c7a05f4c7094f9827c08a2d7143 100755 (executable)
@@ -374,6 +374,7 @@ EXPORT_FUNC(PyMemoryView_FromBuffer)
 EXPORT_FUNC(PyMemoryView_FromMemory)
 EXPORT_FUNC(PyMemoryView_FromObject)
 EXPORT_FUNC(PyMemoryView_GetContiguous)
+EXPORT_FUNC(PyModule_Add)
 EXPORT_FUNC(PyModule_AddFunctions)
 EXPORT_FUNC(PyModule_AddIntConstant)
 EXPORT_FUNC(PyModule_AddObject)
index 3db95f1f07284bbfc0f68dbfbb8ec4a0a30a6983..18b3322ae81d11a9debfd4597c1c0aa20050225b 100644 (file)
@@ -606,13 +606,16 @@ PyModule_AddObjectRef(PyObject *mod, const char *name, PyObject *value)
                      PyModule_GetName(mod));
         return -1;
     }
-
-    if (PyDict_SetItemString(dict, name, value)) {
-        return -1;
-    }
-    return 0;
+    return PyDict_SetItemString(dict, name, value);
 }
 
+int
+PyModule_Add(PyObject *mod, const char *name, PyObject *value)
+{
+    int res = PyModule_AddObjectRef(mod, name, value);
+    Py_XDECREF(value);
+    return res;
+}
 
 int
 PyModule_AddObject(PyObject *mod, const char *name, PyObject *value)
@@ -627,25 +630,13 @@ PyModule_AddObject(PyObject *mod, const char *name, PyObject *value)
 int
 PyModule_AddIntConstant(PyObject *m, const char *name, long value)
 {
-    PyObject *obj = PyLong_FromLong(value);
-    if (!obj) {
-        return -1;
-    }
-    int res = PyModule_AddObjectRef(m, name, obj);
-    Py_DECREF(obj);
-    return res;
+    return PyModule_Add(m, name, PyLong_FromLong(value));
 }
 
 int
 PyModule_AddStringConstant(PyObject *m, const char *name, const char *value)
 {
-    PyObject *obj = PyUnicode_FromString(value);
-    if (!obj) {
-        return -1;
-    }
-    int res = PyModule_AddObjectRef(m, name, obj);
-    Py_DECREF(obj);
-    return res;
+    return PyModule_Add(m, name, PyUnicode_FromString(value));
 }
 
 int