Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit bf2a756

Browse files
committed
gh-105922: Add PyImport_AddModuleRef() function
* Add tests on PyImport_AddModuleRef(), PyImport_AddModule() and PyImport_AddModuleObject(). * pythonrun.c: Replace Py_XNewRef(PyImport_AddModule(name)) with PyImport_AddModuleRef(name).
1 parent a5c2ad0 commit bf2a756

File tree

13 files changed

+151
-26
lines changed

13 files changed

+151
-26
lines changed

Doc/c-api/import.rst

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -98,27 +98,40 @@ Importing Modules
9898
an exception set on failure (the module still exists in this case).
9999
100100
101-
.. c:function:: PyObject* PyImport_AddModuleObject(PyObject *name)
101+
.. c:function:: PyObject* PyImport_AddModuleRef(const char *name)
102+
103+
Return the module object corresponding to a module name.
104+
105+
The *name* argument may be of the form ``package.module``. First check the
106+
modules dictionary if there's one there, and if not, create a new one and
107+
insert it in the modules dictionary.
108+
109+
Return a :term:`strong reference` to the module on success. Return ``NULL``
110+
with an exception set on failure.
102111
103-
Return the module object corresponding to a module name. The *name* argument
104-
may be of the form ``package.module``. First check the modules dictionary if
105-
there's one there, and if not, create a new one and insert it in the modules
106-
dictionary. Return ``NULL`` with an exception set on failure.
112+
The module name *name* is decoded from UTF-8.
107113
108-
.. note::
114+
This function does not load or import the module; if the module wasn't
115+
already loaded, you will get an empty module object. Use
116+
:c:func:`PyImport_ImportModule` or one of its variants to import a module.
117+
Package structures implied by a dotted name for *name* are not created if
118+
not already present.
119+
120+
.. versionadded:: 3.13
121+
122+
123+
.. c:function:: PyObject* PyImport_AddModuleObject(PyObject *name)
109124
110-
This function does not load or import the module; if the module wasn't already
111-
loaded, you will get an empty module object. Use :c:func:`PyImport_ImportModule`
112-
or one of its variants to import a module. Package structures implied by a
113-
dotted name for *name* are not created if not already present.
125+
Similar to :c:func:`PyImport_AddModuleRef`, but return a :term:`borrowed
126+
reference` and *name* is a Python :class:`str` object.
114127
115128
.. versionadded:: 3.3
116129
117130
118131
.. c:function:: PyObject* PyImport_AddModule(const char *name)
119132
120-
Similar to :c:func:`PyImport_AddModuleObject`, but the name is a UTF-8
121-
encoded string instead of a Unicode object.
133+
Similar to :c:func:`PyImport_AddModuleRef`, but return a :term:`borrowed
134+
reference`.
122135
123136
124137
.. c:function:: PyObject* PyImport_ExecCodeModule(const char *name, PyObject *co)

Doc/data/refcounts.dat

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -974,6 +974,9 @@ PyCoro_New:PyFrameObject*:frame:0:
974974
PyCoro_New:PyObject*:name:0:
975975
PyCoro_New:PyObject*:qualname:0:
976976

977+
PyImport_AddModuleRef:PyObject*::+1:
978+
PyImport_AddModuleRef:const char*:name::
979+
977980
PyImport_AddModule:PyObject*::0:reference borrowed from sys.modules
978981
PyImport_AddModule:const char*:name::
979982

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
@@ -426,6 +426,11 @@ New Features
426426
APIs accepting the format codes always use ``Py_ssize_t`` for ``#`` formats.
427427
(Contributed by Inada Naoki in :gh:`104922`.)
428428

429+
* Add :c:func:`PyImport_AddModuleRef`: similar than
430+
:c:func:`PyImport_AddModule`, but return a :term:`strong reference` instead
431+
of a :term:`borrowed reference`.
432+
(Contributed by Victor Stinner in :gh:`105922`.)
433+
429434

430435
Porting to Python 3.13
431436
----------------------

Include/import.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ PyAPI_FUNC(PyObject *) PyImport_AddModuleObject(
4343
PyAPI_FUNC(PyObject *) PyImport_AddModule(
4444
const char *name /* UTF-8 encoded string */
4545
);
46+
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030d0000
47+
PyAPI_FUNC(PyObject *) PyImport_AddModuleRef(
48+
const char *name /* UTF-8 encoded string */
49+
);
50+
#endif
4651
PyAPI_FUNC(PyObject *) PyImport_ImportModule(
4752
const char *name /* UTF-8 encoded string */
4853
);

Lib/test/test_import/__init__.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2621,6 +2621,30 @@ def test_basic_multiple_interpreters_reset_each(self):
26212621
# * module's global state was initialized, not reset
26222622

26232623

2624+
@cpython_only
2625+
class CAPITests(unittest.TestCase):
2626+
def test_pyimport_addmodule(self):
2627+
# gh-105922: Test PyImport_AddModuleRef(), PyImport_AddModule()
2628+
# and PyImport_AddModuleObject()
2629+
import _testcapi
2630+
for name in (
2631+
'sys', # frozen module
2632+
'test', # package
2633+
__name__, # package.module
2634+
):
2635+
_testcapi.check_pyimport_addmodule(name)
2636+
2637+
def test_pyimport_addmodule_create(self):
2638+
# gh-105922: Test PyImport_AddModuleRef(), create a new module
2639+
import _testcapi
2640+
name = 'dontexist'
2641+
self.assertNotIn(name, sys.modules)
2642+
self.addCleanup(unload, name)
2643+
2644+
mod = _testcapi.check_pyimport_addmodule(name)
2645+
self.assertIs(mod, sys.modules[name])
2646+
2647+
26242648
if __name__ == '__main__':
26252649
# Test needs to be a package, so we can do relative imports.
26262650
unittest.main()

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: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Add :c:func:`PyImport_AddModuleRef`: similar than :c:func:`PyImport_AddModule`,
2+
but return a :term:`strong reference` instead of a :term:`borrowed reference`.
3+
Patch by Victor Stinner.

Misc/stable_abi.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2428,3 +2428,5 @@
24282428
added = '3.12'
24292429
[const.Py_TPFLAGS_ITEMS_AT_END]
24302430
added = '3.12'
2431+
[function.PyImport_AddModuleRef]
2432+
added = '3.13'

Modules/_testcapimodule.c

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3325,6 +3325,53 @@ test_atexit(PyObject *self, PyObject *Py_UNUSED(args))
33253325
}
33263326

33273327

3328+
static PyObject *
3329+
check_pyimport_addmodule(PyObject *self, PyObject *args)
3330+
{
3331+
const char *name;
3332+
if (!PyArg_ParseTuple(args, "s", &name)) {
3333+
return NULL;
3334+
}
3335+
3336+
// test PyImport_AddModuleRef()
3337+
PyObject *module = PyImport_AddModuleRef(name);
3338+
if (module == NULL) {
3339+
return NULL;
3340+
}
3341+
assert(PyModule_Check(module));
3342+
// module is a strong reference
3343+
3344+
// test PyImport_AddModule()
3345+
PyObject *module2 = PyImport_AddModule(name);
3346+
if (module2 == NULL) {
3347+
goto error;
3348+
}
3349+
assert(PyModule_Check(module2));
3350+
assert(module2 == module);
3351+
// module2 is a borrowed ref
3352+
3353+
// test PyImport_AddModuleObject()
3354+
PyObject *name_obj = PyUnicode_FromString(name);
3355+
if (name_obj == NULL) {
3356+
goto error;
3357+
}
3358+
PyObject *module3 = PyImport_AddModuleObject(name_obj);
3359+
Py_DECREF(name_obj);
3360+
if (module3 == NULL) {
3361+
goto error;
3362+
}
3363+
assert(PyModule_Check(module3));
3364+
assert(module3 == module);
3365+
// module3 is a borrowed ref
3366+
3367+
return module;
3368+
3369+
error:
3370+
Py_DECREF(module);
3371+
return NULL;
3372+
}
3373+
3374+
33283375
static PyMethodDef TestMethods[] = {
33293376
{"set_errno", set_errno, METH_VARARGS},
33303377
{"test_config", test_config, METH_NOARGS},
@@ -3468,6 +3515,7 @@ static PyMethodDef TestMethods[] = {
34683515
{"function_get_kw_defaults", function_get_kw_defaults, METH_O, NULL},
34693516
{"function_set_kw_defaults", function_set_kw_defaults, METH_VARARGS, NULL},
34703517
{"test_atexit", test_atexit, METH_NOARGS},
3518+
{"check_pyimport_addmodule", check_pyimport_addmodule, METH_VARARGS},
34713519
{NULL, NULL} /* sentinel */
34723520
};
34733521

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/import.c

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -350,20 +350,38 @@ import_add_module(PyThreadState *tstate, PyObject *name)
350350
return m;
351351
}
352352

353+
PyObject *
354+
PyImport_AddModuleRef(const char *name)
355+
{
356+
PyObject *name_obj = PyUnicode_FromString(name);
357+
if (name_obj == NULL) {
358+
return NULL;
359+
}
360+
PyThreadState *tstate = _PyThreadState_GET();
361+
PyObject *module = import_add_module(tstate, name_obj);
362+
Py_DECREF(name_obj);
363+
return module;
364+
}
365+
366+
353367
PyObject *
354368
PyImport_AddModuleObject(PyObject *name)
355369
{
356370
PyThreadState *tstate = _PyThreadState_GET();
357371
PyObject *mod = import_add_module(tstate, name);
358-
if (mod) {
359-
PyObject *ref = PyWeakref_NewRef(mod, NULL);
360-
Py_DECREF(mod);
361-
if (ref == NULL) {
362-
return NULL;
363-
}
364-
mod = PyWeakref_GetObject(ref);
365-
Py_DECREF(ref);
372+
if (!mod) {
373+
return NULL;
366374
}
375+
376+
// gh-86160: PyImport_AddModuleObject() returns a borrowed reference
377+
PyObject *ref = PyWeakref_NewRef(mod, NULL);
378+
Py_DECREF(mod);
379+
if (ref == NULL) {
380+
return NULL;
381+
}
382+
383+
mod = PyWeakref_GetObject(ref);
384+
Py_DECREF(ref);
367385
return mod; /* borrowed reference */
368386
}
369387

@@ -1364,8 +1382,8 @@ create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec)
13641382
if (_PyUnicode_EqualToASCIIString(name, p->name)) {
13651383
if (p->initfunc == NULL) {
13661384
/* Cannot re-init internal module ("sys" or "builtins") */
1367-
mod = PyImport_AddModuleObject(name);
1368-
return Py_XNewRef(mod);
1385+
PyThreadState *tstate = _PyThreadState_GET();
1386+
return import_add_module(tstate, name);
13691387
}
13701388
mod = _PyImport_InitFunc_TrampolineCall(*p->initfunc);
13711389
if (mod == NULL) {
@@ -2240,11 +2258,12 @@ init_importlib(PyThreadState *tstate, PyObject *sysmod)
22402258
if (PyImport_ImportFrozenModule("_frozen_importlib") <= 0) {
22412259
return -1;
22422260
}
2243-
PyObject *importlib = PyImport_AddModule("_frozen_importlib"); // borrowed
2261+
2262+
PyObject *importlib = PyImport_AddModuleRef("_frozen_importlib");
22442263
if (importlib == NULL) {
22452264
return -1;
22462265
}
2247-
IMPORTLIB(interp) = Py_NewRef(importlib);
2266+
IMPORTLIB(interp) = importlib;
22482267

22492268
// Import the _imp module
22502269
if (verbose) {

Python/pythonrun.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ _PyRun_SimpleFileObject(FILE *fp, PyObject *filename, int closeit,
406406
{
407407
int ret = -1;
408408

409-
PyObject *main_module = Py_XNewRef(PyImport_AddModule("__main__"));
409+
PyObject *main_module = PyImport_AddModuleRef("__main__");
410410
if (main_module == NULL)
411411
return -1;
412412
PyObject *dict = PyModule_GetDict(main_module); // borrowed ref
@@ -502,7 +502,7 @@ PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit,
502502
int
503503
PyRun_SimpleStringFlags(const char *command, PyCompilerFlags *flags)
504504
{
505-
PyObject *main_module = Py_XNewRef(PyImport_AddModule("__main__"));
505+
PyObject *main_module = PyImport_AddModuleRef("__main__");
506506
if (main_module == NULL) {
507507
return -1;
508508
}

0 commit comments

Comments
 (0)