Skip to content

Commit 83ac128

Browse files
bpo-42327: C API: Add PyModule_Add() function (GH-23443)
It is a fixed implementation of PyModule_AddObject() which consistently steals reference both on success and on failure.
1 parent 7454923 commit 83ac128

File tree

9 files changed

+61
-56
lines changed

9 files changed

+61
-56
lines changed

Doc/c-api/module.rst

Lines changed: 31 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -486,12 +486,29 @@ state:
486486
.. versionadded:: 3.10
487487
488488
489+
.. c:function:: int PyModule_Add(PyObject *module, const char *name, PyObject *value)
490+
491+
Similar to :c:func:`PyModule_AddObjectRef`, but "steals" a reference
492+
to *value*.
493+
It can be called with a result of function that returns a new reference
494+
without bothering to check its result or even saving it to a variable.
495+
496+
Example usage::
497+
498+
if (PyModule_Add(module, "spam", PyBytes_FromString(value)) < 0) {
499+
goto error;
500+
}
501+
502+
.. versionadded:: 3.13
503+
504+
489505
.. c:function:: int PyModule_AddObject(PyObject *module, const char *name, PyObject *value)
490506
491507
Similar to :c:func:`PyModule_AddObjectRef`, but steals a reference to
492508
*value* on success (if it returns ``0``).
493509
494-
The new :c:func:`PyModule_AddObjectRef` function is recommended, since it is
510+
The new :c:func:`PyModule_Add` or :c:func:`PyModule_AddObjectRef`
511+
functions are recommended, since it is
495512
easy to introduce reference leaks by misusing the
496513
:c:func:`PyModule_AddObject` function.
497514
@@ -501,44 +518,24 @@ state:
501518
only decrements the reference count of *value* **on success**.
502519
503520
This means that its return value must be checked, and calling code must
504-
:c:func:`Py_DECREF` *value* manually on error.
521+
:c:func:`Py_XDECREF` *value* manually on error.
505522
506523
Example usage::
507524
508-
static int
509-
add_spam(PyObject *module, int value)
510-
{
511-
PyObject *obj = PyLong_FromLong(value);
512-
if (obj == NULL) {
513-
return -1;
514-
}
515-
if (PyModule_AddObject(module, "spam", obj) < 0) {
516-
Py_DECREF(obj);
517-
return -1;
518-
}
519-
// PyModule_AddObject() stole a reference to obj:
520-
// Py_DECREF(obj) is not needed here
521-
return 0;
522-
}
523-
524-
The example can also be written without checking explicitly if *obj* is
525-
``NULL``::
525+
PyObject *obj = PyBytes_FromString(value);
526+
if (PyModule_AddObject(module, "spam", obj) < 0) {
527+
// If 'obj' is not NULL and PyModule_AddObject() failed,
528+
// 'obj' strong reference must be deleted with Py_XDECREF().
529+
// If 'obj' is NULL, Py_XDECREF() does nothing.
530+
Py_XDECREF(obj);
531+
goto error;
532+
}
533+
// PyModule_AddObject() stole a reference to obj:
534+
// Py_XDECREF(obj) is not needed here.
526535
527-
static int
528-
add_spam(PyObject *module, int value)
529-
{
530-
PyObject *obj = PyLong_FromLong(value);
531-
if (PyModule_AddObject(module, "spam", obj) < 0) {
532-
Py_XDECREF(obj);
533-
return -1;
534-
}
535-
// PyModule_AddObject() stole a reference to obj:
536-
// Py_DECREF(obj) is not needed here
537-
return 0;
538-
}
536+
.. deprecated:: 3.13
539537
540-
Note that ``Py_XDECREF()`` should be used instead of ``Py_DECREF()`` in
541-
this case, since *obj* can be ``NULL``.
538+
:c:func:`PyModule_AddObject` is :term:`soft deprecated`.
542539
543540
544541
.. c:function:: int PyModule_AddIntConstant(PyObject *module, const char *name, long value)

Doc/data/stable_abi.dat

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Doc/whatsnew/3.13.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,11 @@ New Features
774774
If the assertion fails, make sure that the size is set before.
775775
(Contributed by Victor Stinner in :gh:`106168`.)
776776

777+
* Add :c:func:`PyModule_Add` function: similar to
778+
:c:func:`PyModule_AddObjectRef` and :c:func:`PyModule_AddObject` but
779+
always steals a reference to the value.
780+
(Contributed by Serhiy Storchaka in :gh:`86493`.)
781+
777782
Porting to Python 3.13
778783
----------------------
779784

Include/modsupport.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,18 @@ PyAPI_FUNC(PyObject *) Py_BuildValue(const char *, ...);
2323
PyAPI_FUNC(PyObject *) Py_VaBuildValue(const char *, va_list);
2424

2525
// Add an attribute with name 'name' and value 'obj' to the module 'mod.
26-
// On success, return 0 on success.
26+
// On success, return 0.
2727
// On error, raise an exception and return -1.
2828
PyAPI_FUNC(int) PyModule_AddObjectRef(PyObject *mod, const char *name, PyObject *value);
2929

30-
// Similar to PyModule_AddObjectRef() but steal a reference to 'obj'
31-
// (Py_DECREF(obj)) on success (if it returns 0).
30+
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030d0000
31+
// Similar to PyModule_AddObjectRef() but steal a reference to 'value'.
32+
PyAPI_FUNC(int) PyModule_Add(PyObject *mod, const char *name, PyObject *value);
33+
#endif /* Py_LIMITED_API */
34+
35+
// Similar to PyModule_AddObjectRef() and PyModule_Add() but steal
36+
// a reference to 'value' on success and only on success.
37+
// Errorprone. Should not be used in new code.
3238
PyAPI_FUNC(int) PyModule_AddObject(PyObject *mod, const char *, PyObject *value);
3339

3440
PyAPI_FUNC(int) PyModule_AddIntConstant(PyObject *, const char *, long);

Lib/test/test_stable_abi_ctypes.py

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
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.

Misc/stable_abi.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2444,3 +2444,5 @@
24442444
added = '3.13'
24452445
[function.PyMapping_GetOptionalItemString]
24462446
added = '3.13'
2447+
[function.PyModule_Add]
2448+
added = '3.13'

PC/python3dll.c

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/modsupport.c

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -606,13 +606,16 @@ PyModule_AddObjectRef(PyObject *mod, const char *name, PyObject *value)
606606
PyModule_GetName(mod));
607607
return -1;
608608
}
609-
610-
if (PyDict_SetItemString(dict, name, value)) {
611-
return -1;
612-
}
613-
return 0;
609+
return PyDict_SetItemString(dict, name, value);
614610
}
615611

612+
int
613+
PyModule_Add(PyObject *mod, const char *name, PyObject *value)
614+
{
615+
int res = PyModule_AddObjectRef(mod, name, value);
616+
Py_XDECREF(value);
617+
return res;
618+
}
616619

617620
int
618621
PyModule_AddObject(PyObject *mod, const char *name, PyObject *value)
@@ -627,25 +630,13 @@ PyModule_AddObject(PyObject *mod, const char *name, PyObject *value)
627630
int
628631
PyModule_AddIntConstant(PyObject *m, const char *name, long value)
629632
{
630-
PyObject *obj = PyLong_FromLong(value);
631-
if (!obj) {
632-
return -1;
633-
}
634-
int res = PyModule_AddObjectRef(m, name, obj);
635-
Py_DECREF(obj);
636-
return res;
633+
return PyModule_Add(m, name, PyLong_FromLong(value));
637634
}
638635

639636
int
640637
PyModule_AddStringConstant(PyObject *m, const char *name, const char *value)
641638
{
642-
PyObject *obj = PyUnicode_FromString(value);
643-
if (!obj) {
644-
return -1;
645-
}
646-
int res = PyModule_AddObjectRef(m, name, obj);
647-
Py_DECREF(obj);
648-
return res;
639+
return PyModule_Add(m, name, PyUnicode_FromString(value));
649640
}
650641

651642
int

0 commit comments

Comments
 (0)