Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Cleanup and update docs.
  • Loading branch information
serhiy-storchaka committed Jul 10, 2023
commit 37ff11b7dd6cb46c95ea86694dfe5b541fdebf00
20 changes: 7 additions & 13 deletions Doc/c-api/module.rst
Original file line number Diff line number Diff line change
Expand Up @@ -488,17 +488,10 @@ state:

.. c:function:: int PyModule_AddNew(PyObject *module, const char *name, PyObject *value)

Add an object to *module* as *name*. This is a convenience function which
can be used from the module's initialization function.

On success, return ``0``. On error, raise an exception and return ``-1``.

Return ``-1`` if *value* is ``NULL``. It must be called with an exception
raised in this case.

This function "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.
Similar to :c:func:`PyModule_AddObjectRef`, but "steals" a reference
to *value*.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PyModule_AddObjectRef() must not be called with NULL. Can you please elaborate the behavior when value is NULL? Something like:

If value is NULL, just return -1.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PyModule_AddObjectRef() can be called with NULL if an exception is set. Actually, some of my fixes which use PyModule_AddObjectRef() rely on this.

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::

Expand All @@ -511,10 +504,11 @@ state:

.. c:function:: int PyModule_AddObject(PyObject *module, const char *name, PyObject *value)

Similar to :c:func:`PyModule_AddNew`, but only steals a reference to
Similar to :c:func:`PyModule_AddObjectRef`, but steals a reference to
*value* on success (if it returns ``0``).

The new :c:func:`PyModule_AddNew` function is recommended, since it is
The new :c:func:`PyModule_AddNew` or :c:func:`PyModule_AddObjectRef`
functions are recommended, since it is
easy to introduce reference leaks by misusing the
:c:func:`PyModule_AddObject` function.

Expand Down
3 changes: 2 additions & 1 deletion Doc/whatsnew/3.13.rst
Original file line number Diff line number Diff line change
Expand Up @@ -740,7 +740,8 @@ New Features
(Contributed by Victor Stinner in :gh:`106168`.)

* Add :c:func:`PyModule_AddNew` function: similar to
:c:func:`PyModule_AddObject` but always steals a reference to the value.
: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
Expand Down
17 changes: 8 additions & 9 deletions Include/modsupport.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,19 @@ PyAPI_FUNC(int) PyArg_UnpackTuple(PyObject *, const char *, Py_ssize_t, Py_ssize
PyAPI_FUNC(PyObject *) Py_BuildValue(const char *, ...);
PyAPI_FUNC(PyObject *) Py_VaBuildValue(const char *, va_list);

#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030A0000
// Add an attribute with name 'name' and value 'value' to the module 'mod'.
// Steal a reference to 'value'.
// Add an attribute with name 'name' and value 'obj' to the module 'mod.
// On success, return 0.
// On error, raise an exception and return -1.
PyAPI_FUNC(int) PyModule_AddObjectRef(PyObject *mod, const char *name, PyObject *value);

#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030d0000
// Similar to PyModule_AddObjectRef() but steal a reference to 'value'.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest to add "and value can be NULL".

PyAPI_FUNC(int) PyModule_AddNew(PyObject *mod, const char *name, PyObject *value);
#endif /* Py_LIMITED_API */

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

// Similar to PyModule_AddNew() but steal a reference to 'value' only on success.
// Similar to PyModule_AddObjectRef() and PyModule_AddNew() but steal
// a reference to 'value' on success and only on success.
// Errorprone. Should not be used in new code.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO it's important important to put it in the documentation. This kind of definition fits perfectly with the Soft Deprecated status. Maybe also soft deprecate this API, similar to getopt and optparse soft deprecation. Example with getopt:

.. deprecated:: 3.13
   The :mod:`getopt` module is :term:`soft deprecated` and will not be
   developed further; development will continue with the :mod:`argparse`
   module.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you suggest to convert the note block into deprecated?

PyAPI_FUNC(int) PyModule_AddObject(PyObject *mod, const char *, PyObject *value);

PyAPI_FUNC(int) PyModule_AddIntConstant(PyObject *, const char *, long);
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Added :func:`PyModule_AddNew`.
Add :func:`PyModule_AddNew` function.
2 changes: 1 addition & 1 deletion Modules/_curses_panel.c
Original file line number Diff line number Diff line change
Expand Up @@ -662,7 +662,7 @@ _curses_panel_exec(PyObject *mod)
state->PyCursesError = PyErr_NewException(
"_curses_panel.error", NULL, NULL);

if (PyModule_AddNew(mod, "error", Py_XNewRef(state->PyCursesError)) < 0) {
if (PyModule_AddObjectRef(mod, "error", state->PyCursesError) < 0) {
return -1;
}

Expand Down
12 changes: 6 additions & 6 deletions Modules/_stat.c
Original file line number Diff line number Diff line change
Expand Up @@ -592,16 +592,16 @@ stat_exec(PyObject *module)
ADD_INT_MACRO(module, FILE_ATTRIBUTE_VIRTUAL);

if (PyModule_AddNew(module, "IO_REPARSE_TAG_SYMLINK",
PyLong_FromUnsignedLong(IO_REPARSE_TAG_SYMLINK)) < 0) {
return -1;
PyLong_FromUnsignedLong(IO_REPARSE_TAG_SYMLINK)) < 0) {
return -1;
}
if (PyModule_AddNew(module, "IO_REPARSE_TAG_MOUNT_POINT",
PyLong_FromUnsignedLong(IO_REPARSE_TAG_MOUNT_POINT)) < 0) {
return -1;
PyLong_FromUnsignedLong(IO_REPARSE_TAG_MOUNT_POINT)) < 0) {
return -1;
}
if (PyModule_AddNew(module, "IO_REPARSE_TAG_APPEXECLINK",
PyLong_FromUnsignedLong(IO_REPARSE_TAG_APPEXECLINK)) < 0) {
return -1;
PyLong_FromUnsignedLong(IO_REPARSE_TAG_APPEXECLINK)) < 0) {
return -1;
}
#endif

Expand Down
4 changes: 2 additions & 2 deletions Modules/_testinternalcapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -1380,12 +1380,12 @@ static int
module_exec(PyObject *module)
{
if (PyModule_AddNew(module, "SIZEOF_PYGC_HEAD",
PyLong_FromSsize_t(sizeof(PyGC_Head))) < 0) {
PyLong_FromSsize_t(sizeof(PyGC_Head))) < 0) {
return 1;
}

if (PyModule_AddNew(module, "SIZEOF_TIME_T",
PyLong_FromSsize_t(sizeof(time_t))) < 0) {
PyLong_FromSsize_t(sizeof(time_t))) < 0) {
return 1;
}

Expand Down
2 changes: 1 addition & 1 deletion Modules/_threadmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1672,7 +1672,7 @@ thread_module_exec(PyObject *module)
timeout_max = floor(timeout_max);

if (PyModule_AddNew(module, "TIMEOUT_MAX",
PyFloat_FromDouble(timeout_max)) < 0) {
PyFloat_FromDouble(timeout_max)) < 0) {
return -1;
}

Expand Down
6 changes: 5 additions & 1 deletion Modules/cjkcodecs/cjkcodecs.h
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,11 @@ register_maps(PyObject *module)
strcpy(mhname + sizeof("__map_") - 1, h->charset);

PyObject *capsule = PyCapsule_New((void *)h, MAP_CAPSULE, NULL);
if (PyModule_AddNew(module, mhname, capsule) < 0) {
if (capsule == NULL) {
return -1;
}
if (PyModule_AddObject(module, mhname, capsule) < 0) {
Py_DECREF(capsule);
return -1;
}
}
Expand Down
4 changes: 2 additions & 2 deletions Modules/xxsubtype.c
Original file line number Diff line number Diff line change
Expand Up @@ -274,10 +274,10 @@ xxsubtype_exec(PyObject* m)
if (PyType_Ready(&spamdict_type) < 0)
return -1;

if (PyModule_AddNew(m, "spamlist", Py_NewRef(&spamlist_type)) < 0)
if (PyModule_AddObjectRef(m, "spamlist", (PyObject *)&spamlist_type) < 0)
return -1;

if (PyModule_AddNew(m, "spamdict", Py_NewRef(&spamdict_type)) < 0)
if (PyModule_AddObjectRef(m, "spamdict", (PyObject *)&spamdict_type) < 0)
return -1;
return 0;
}
Expand Down
45 changes: 20 additions & 25 deletions Python/modsupport.c
Original file line number Diff line number Diff line change
Expand Up @@ -581,52 +581,48 @@ _Py_VaBuildStack(PyObject **small_stack, Py_ssize_t small_stack_len,
}


static int
add_object(PyObject *m, const char *name, PyObject *o)
int
PyModule_AddObjectRef(PyObject *mod, const char *name, PyObject *value)
{
PyObject *dict;
if (!PyModule_Check(m)) {
if (!PyModule_Check(mod)) {
PyErr_SetString(PyExc_TypeError,
"PyModule_AddNew() needs module as first arg");
"PyModule_AddObjectRef() first argument "
"must be a module");
return -1;
}
if (!o) {
if (!PyErr_Occurred())
if (!value) {
if (!PyErr_Occurred()) {
PyErr_SetString(PyExc_SystemError,
"PyModule_AddNew() needs non-NULL value or exception set");
"PyModule_AddObjectRef() must be called "
"with an exception raised if value is NULL");
}
return -1;
}

dict = PyModule_GetDict(m);
PyObject *dict = PyModule_GetDict(mod);
if (dict == NULL) {
/* Internal error -- modules must have a dict! */
PyErr_Format(PyExc_SystemError, "module '%s' has no __dict__",
PyModule_GetName(m));
PyModule_GetName(mod));
return -1;
}
return PyDict_SetItemString(dict, name, o);
}

int
PyModule_AddObjectRef(PyObject *mod, const char *name, PyObject *value)
{
return add_object(mod, name, value);
return PyDict_SetItemString(dict, name, value);
}

int
PyModule_AddNew(PyObject *m, const char *name, PyObject *o)
PyModule_AddNew(PyObject *mod, const char *name, PyObject *value)
{
int res = add_object(m, name, o);
Py_XDECREF(o);
int res = PyModule_AddObjectRef(mod, name, value);
Py_XDECREF(value);
return res;
}

int
PyModule_AddObject(PyObject *m, const char *name, PyObject *o)
PyModule_AddObject(PyObject *mod, const char *name, PyObject *value)
{
int res = add_object(m, name, o);
int res = PyModule_AddObjectRef(mod, name, value);
if (res == 0) {
Py_DECREF(o);
Py_DECREF(value);
}
return res;
}
Expand All @@ -653,6 +649,5 @@ PyModule_AddType(PyObject *module, PyTypeObject *type)
const char *name = _PyType_Name(type);
assert(name != NULL);

Py_INCREF(type);
return PyModule_AddNew(module, name, (PyObject *)type);
return PyModule_AddObjectRef(module, name, (PyObject *)type);
}