Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit e3f0eba

Browse filesBrowse files
committed
Add PyModule_Add() function
PyModule_AddObjectRef() now raises SystemError if the value is NULL and no exception is set.
1 parent 7515dae commit e3f0eba
Copy full SHA for e3f0eba

File tree

4 files changed

+93
-15
lines changed
Filter options

4 files changed

+93
-15
lines changed

‎docs/api.rst

Copy file name to clipboardExpand all lines: docs/api.rst
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ Python 3.13
5151
5252
See `PyMapping_GetOptionalItemString() documentation <https://docs.python.org/dev/c-api/mapping.html#c.PyMapping_GetOptionalItemString>`__.
5353
54+
.. c:function:: int PyModule_Add(PyObject *module, const char *name, PyObject *value)
55+
56+
See `PyModule_Add() documentation <https://docs.python.org/dev/c-api/module.html#c.PyModule_Add>`__.
57+
5458
5559
Python 3.12
5660
-----------

‎docs/changelog.rst

Copy file name to clipboardExpand all lines: docs/changelog.rst
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
Changelog
22
=========
33

4+
* 2023-07-18: Add ``PyModule_Add()`` function.
45
* 2023-07-12: Add ``PyObject_GetOptionalAttr()``,
56
``PyObject_GetOptionalAttrString()``,
67
``PyMapping_GetOptionalItem()``

‎pythoncapi_compat.h

Copy file name to clipboardExpand all lines: pythoncapi_compat.h
+21Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,15 @@ PYCAPI_COMPAT_STATIC_INLINE(int)
422422
PyModule_AddObjectRef(PyObject *module, const char *name, PyObject *value)
423423
{
424424
int res;
425+
426+
if (!value && !PyErr_Occurred()) {
427+
// PyModule_AddObject() raises TypeError in this case
428+
PyErr_SetString(PyExc_SystemError,
429+
"PyModule_AddObjectRef() must be called "
430+
"with an exception raised if value is NULL");
431+
return -1;
432+
}
433+
425434
Py_XINCREF(value);
426435
res = PyModule_AddObject(module, name, value);
427436
if (res < 0) {
@@ -782,6 +791,18 @@ PyMapping_GetOptionalItemString(PyObject *obj, const char *key, PyObject **resul
782791
#endif
783792

784793

794+
// gh-106307 added PyModule_Add() to Python 3.13.0a1
795+
#if PY_VERSION_HEX < 0x030D00A1
796+
PYCAPI_COMPAT_STATIC_INLINE(int)
797+
PyModule_Add(PyObject *mod, const char *name, PyObject *value)
798+
{
799+
int res = PyModule_AddObjectRef(mod, name, value);
800+
Py_XDECREF(value);
801+
return res;
802+
}
803+
#endif
804+
805+
785806
#ifdef __cplusplus
786807
}
787808
#endif

‎tests/test_pythoncapi_compat_cext.c

Copy file name to clipboardExpand all lines: tests/test_pythoncapi_compat_cext.c
+67-15Lines changed: 67 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,23 @@ test_gc(PyObject *Py_UNUSED(module), PyObject* Py_UNUSED(ignored))
386386
}
387387

388388

389+
static int
390+
check_module_attr(PyObject *module, const char *name, PyObject *expected)
391+
{
392+
PyObject *attr = PyObject_GetAttrString(module, name);
393+
if (attr == _Py_NULL) {
394+
return -1;
395+
}
396+
assert(attr == expected);
397+
Py_DECREF(attr);
398+
399+
if (PyObject_DelAttrString(module, name) < 0) {
400+
return -1;
401+
}
402+
return 0;
403+
}
404+
405+
389406
// test PyModule_AddType()
390407
static int
391408
test_module_add_type(PyObject *module)
@@ -407,14 +424,7 @@ test_module_add_type(PyObject *module)
407424
ASSERT_REFCNT(Py_REFCNT(type) == refcnt + 1);
408425
#endif
409426

410-
PyObject *attr = PyObject_GetAttrString(module, type_name);
411-
if (attr == _Py_NULL) {
412-
return -1;
413-
}
414-
assert(attr == _Py_CAST(PyObject*, type));
415-
Py_DECREF(attr);
416-
417-
if (PyObject_DelAttrString(module, type_name) < 0) {
427+
if (check_module_attr(module, type_name, _Py_CAST(PyObject*, type)) < 0) {
418428
return -1;
419429
}
420430
ASSERT_REFCNT(Py_REFCNT(type) == refcnt);
@@ -426,30 +436,70 @@ test_module_add_type(PyObject *module)
426436
static int
427437
test_module_addobjectref(PyObject *module)
428438
{
429-
PyObject *obj = Py_True;
430439
const char *name = "test_module_addobjectref";
440+
PyObject *obj = PyUnicode_FromString(name);
441+
assert(obj != NULL);
431442
#ifdef CHECK_REFCNT
432443
Py_ssize_t refcnt = Py_REFCNT(obj);
433444
#endif
434445

435446
if (PyModule_AddObjectRef(module, name, obj) < 0) {
436447
ASSERT_REFCNT(Py_REFCNT(obj) == refcnt);
448+
Py_DECREF(obj);
437449
return -1;
438450
}
439-
#ifndef IMMORTAL_OBJS
440451
ASSERT_REFCNT(Py_REFCNT(obj) == refcnt + 1);
441-
#endif
442452

443-
if (PyObject_DelAttrString(module, name) < 0) {
453+
if (check_module_attr(module, name, obj) < 0) {
454+
Py_DECREF(obj);
444455
return -1;
445456
}
446457
ASSERT_REFCNT(Py_REFCNT(obj) == refcnt);
447458

448459
// PyModule_AddObjectRef() with value=NULL must not crash
460+
assert(!PyErr_Occurred());
449461
int res = PyModule_AddObjectRef(module, name, _Py_NULL);
450462
assert(res < 0);
463+
assert(PyErr_ExceptionMatches(PyExc_SystemError));
464+
PyErr_Clear();
465+
466+
Py_DECREF(obj);
467+
return 0;
468+
}
469+
470+
471+
// test PyModule_Add()
472+
static int
473+
test_module_add(PyObject *module)
474+
{
475+
const char *name = "test_module_add";
476+
PyObject *obj = PyUnicode_FromString(name);
477+
assert(obj != NULL);
478+
#ifdef CHECK_REFCNT
479+
Py_ssize_t refcnt = Py_REFCNT(obj);
480+
#endif
481+
482+
if (PyModule_Add(module, name, Py_NewRef(obj)) < 0) {
483+
ASSERT_REFCNT(Py_REFCNT(obj) == refcnt);
484+
Py_DECREF(obj);
485+
return -1;
486+
}
487+
ASSERT_REFCNT(Py_REFCNT(obj) == refcnt + 1);
488+
489+
if (check_module_attr(module, name, obj) < 0) {
490+
Py_DECREF(obj);
491+
return -1;
492+
}
493+
ASSERT_REFCNT(Py_REFCNT(obj) == refcnt);
494+
495+
// PyModule_Add() with value=NULL must not crash
496+
assert(!PyErr_Occurred());
497+
int res = PyModule_Add(module, name, _Py_NULL);
498+
assert(res < 0);
499+
assert(PyErr_ExceptionMatches(PyExc_SystemError));
451500
PyErr_Clear();
452501

502+
Py_DECREF(obj);
453503
return 0;
454504
}
455505

@@ -458,18 +508,20 @@ static PyObject *
458508
test_module(PyObject *Py_UNUSED(module), PyObject* Py_UNUSED(ignored))
459509
{
460510
PyObject *module = PyImport_ImportModule("sys");
461-
if (module == _Py_NULL) {
462-
return _Py_NULL;
511+
if (module == NULL) {
512+
return NULL;
463513
}
464514
assert(PyModule_Check(module));
465515

466516
if (test_module_add_type(module) < 0) {
467517
goto error;
468518
}
469-
470519
if (test_module_addobjectref(module) < 0) {
471520
goto error;
472521
}
522+
if (test_module_add(module) < 0) {
523+
goto error;
524+
}
473525

474526
Py_DECREF(module);
475527
Py_RETURN_NONE;

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.