mirror of https://github.com/python/cpython
gh-105922: Add PyImport_AddModuleRef() function (#105923)
* Add tests on PyImport_AddModuleRef(), PyImport_AddModule() and PyImport_AddModuleObject(). * pythonrun.c: Replace Py_XNewRef(PyImport_AddModule(name)) with PyImport_AddModuleRef(name).
This commit is contained in:
parent
7f97c8e367
commit
03f1a132ee
|
@ -98,27 +98,40 @@ Importing Modules
|
|||
an exception set on failure (the module still exists in this case).
|
||||
|
||||
|
||||
.. c:function:: PyObject* PyImport_AddModuleRef(const char *name)
|
||||
|
||||
Return the module object corresponding to a module name.
|
||||
|
||||
The *name* argument may be of the form ``package.module``. First check the
|
||||
modules dictionary if there's one there, and if not, create a new one and
|
||||
insert it in the modules dictionary.
|
||||
|
||||
Return a :term:`strong reference` to the module on success. Return ``NULL``
|
||||
with an exception set on failure.
|
||||
|
||||
The module name *name* is decoded from UTF-8.
|
||||
|
||||
This function does not load or import the module; if the module wasn't
|
||||
already loaded, you will get an empty module object. Use
|
||||
:c:func:`PyImport_ImportModule` or one of its variants to import a module.
|
||||
Package structures implied by a dotted name for *name* are not created if
|
||||
not already present.
|
||||
|
||||
.. versionadded:: 3.13
|
||||
|
||||
|
||||
.. c:function:: PyObject* PyImport_AddModuleObject(PyObject *name)
|
||||
|
||||
Return the module object corresponding to a module name. The *name* argument
|
||||
may be of the form ``package.module``. First check the modules dictionary if
|
||||
there's one there, and if not, create a new one and insert it in the modules
|
||||
dictionary. Return ``NULL`` with an exception set on failure.
|
||||
|
||||
.. note::
|
||||
|
||||
This function does not load or import the module; if the module wasn't already
|
||||
loaded, you will get an empty module object. Use :c:func:`PyImport_ImportModule`
|
||||
or one of its variants to import a module. Package structures implied by a
|
||||
dotted name for *name* are not created if not already present.
|
||||
Similar to :c:func:`PyImport_AddModuleRef`, but return a :term:`borrowed
|
||||
reference` and *name* is a Python :class:`str` object.
|
||||
|
||||
.. versionadded:: 3.3
|
||||
|
||||
|
||||
.. c:function:: PyObject* PyImport_AddModule(const char *name)
|
||||
|
||||
Similar to :c:func:`PyImport_AddModuleObject`, but the name is a UTF-8
|
||||
encoded string instead of a Unicode object.
|
||||
Similar to :c:func:`PyImport_AddModuleRef`, but return a :term:`borrowed
|
||||
reference`.
|
||||
|
||||
|
||||
.. c:function:: PyObject* PyImport_ExecCodeModule(const char *name, PyObject *co)
|
||||
|
|
|
@ -974,6 +974,9 @@ PyCoro_New:PyFrameObject*:frame:0:
|
|||
PyCoro_New:PyObject*:name:0:
|
||||
PyCoro_New:PyObject*:qualname:0:
|
||||
|
||||
PyImport_AddModuleRef:PyObject*::+1:
|
||||
PyImport_AddModuleRef:const char*:name::
|
||||
|
||||
PyImport_AddModule:PyObject*::0:reference borrowed from sys.modules
|
||||
PyImport_AddModule:const char*:name::
|
||||
|
||||
|
|
|
@ -298,6 +298,7 @@ type,PyGetSetDef,3.2,,full-abi
|
|||
var,PyGetSetDescr_Type,3.2,,
|
||||
function,PyImport_AddModule,3.2,,
|
||||
function,PyImport_AddModuleObject,3.7,,
|
||||
function,PyImport_AddModuleRef,3.13,,
|
||||
function,PyImport_AppendInittab,3.2,,
|
||||
function,PyImport_ExecCodeModule,3.2,,
|
||||
function,PyImport_ExecCodeModuleEx,3.2,,
|
||||
|
|
|
@ -426,6 +426,11 @@ New Features
|
|||
APIs accepting the format codes always use ``Py_ssize_t`` for ``#`` formats.
|
||||
(Contributed by Inada Naoki in :gh:`104922`.)
|
||||
|
||||
* Add :c:func:`PyImport_AddModuleRef`: similar to
|
||||
:c:func:`PyImport_AddModule`, but return a :term:`strong reference` instead
|
||||
of a :term:`borrowed reference`.
|
||||
(Contributed by Victor Stinner in :gh:`105922`.)
|
||||
|
||||
|
||||
Porting to Python 3.13
|
||||
----------------------
|
||||
|
|
|
@ -43,6 +43,11 @@ PyAPI_FUNC(PyObject *) PyImport_AddModuleObject(
|
|||
PyAPI_FUNC(PyObject *) PyImport_AddModule(
|
||||
const char *name /* UTF-8 encoded string */
|
||||
);
|
||||
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030d0000
|
||||
PyAPI_FUNC(PyObject *) PyImport_AddModuleRef(
|
||||
const char *name /* UTF-8 encoded string */
|
||||
);
|
||||
#endif
|
||||
PyAPI_FUNC(PyObject *) PyImport_ImportModule(
|
||||
const char *name /* UTF-8 encoded string */
|
||||
);
|
||||
|
|
|
@ -2621,6 +2621,30 @@ class SinglephaseInitTests(unittest.TestCase):
|
|||
# * module's global state was initialized, not reset
|
||||
|
||||
|
||||
@cpython_only
|
||||
class CAPITests(unittest.TestCase):
|
||||
def test_pyimport_addmodule(self):
|
||||
# gh-105922: Test PyImport_AddModuleRef(), PyImport_AddModule()
|
||||
# and PyImport_AddModuleObject()
|
||||
import _testcapi
|
||||
for name in (
|
||||
'sys', # frozen module
|
||||
'test', # package
|
||||
__name__, # package.module
|
||||
):
|
||||
_testcapi.check_pyimport_addmodule(name)
|
||||
|
||||
def test_pyimport_addmodule_create(self):
|
||||
# gh-105922: Test PyImport_AddModuleRef(), create a new module
|
||||
import _testcapi
|
||||
name = 'dontexist'
|
||||
self.assertNotIn(name, sys.modules)
|
||||
self.addCleanup(unload, name)
|
||||
|
||||
mod = _testcapi.check_pyimport_addmodule(name)
|
||||
self.assertIs(mod, sys.modules[name])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Test needs to be a package, so we can do relative imports.
|
||||
unittest.main()
|
||||
|
|
|
@ -328,6 +328,7 @@ SYMBOL_NAMES = (
|
|||
"PyGetSetDescr_Type",
|
||||
"PyImport_AddModule",
|
||||
"PyImport_AddModuleObject",
|
||||
"PyImport_AddModuleRef",
|
||||
"PyImport_AppendInittab",
|
||||
"PyImport_ExecCodeModule",
|
||||
"PyImport_ExecCodeModuleEx",
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
Add :c:func:`PyImport_AddModuleRef`: similar to :c:func:`PyImport_AddModule`,
|
||||
but return a :term:`strong reference` instead of a :term:`borrowed reference`.
|
||||
Patch by Victor Stinner.
|
|
@ -2428,3 +2428,5 @@
|
|||
added = '3.12'
|
||||
[const.Py_TPFLAGS_ITEMS_AT_END]
|
||||
added = '3.12'
|
||||
[function.PyImport_AddModuleRef]
|
||||
added = '3.13'
|
||||
|
|
|
@ -3325,6 +3325,53 @@ test_atexit(PyObject *self, PyObject *Py_UNUSED(args))
|
|||
}
|
||||
|
||||
|
||||
static PyObject *
|
||||
check_pyimport_addmodule(PyObject *self, PyObject *args)
|
||||
{
|
||||
const char *name;
|
||||
if (!PyArg_ParseTuple(args, "s", &name)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// test PyImport_AddModuleRef()
|
||||
PyObject *module = PyImport_AddModuleRef(name);
|
||||
if (module == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
assert(PyModule_Check(module));
|
||||
// module is a strong reference
|
||||
|
||||
// test PyImport_AddModule()
|
||||
PyObject *module2 = PyImport_AddModule(name);
|
||||
if (module2 == NULL) {
|
||||
goto error;
|
||||
}
|
||||
assert(PyModule_Check(module2));
|
||||
assert(module2 == module);
|
||||
// module2 is a borrowed ref
|
||||
|
||||
// test PyImport_AddModuleObject()
|
||||
PyObject *name_obj = PyUnicode_FromString(name);
|
||||
if (name_obj == NULL) {
|
||||
goto error;
|
||||
}
|
||||
PyObject *module3 = PyImport_AddModuleObject(name_obj);
|
||||
Py_DECREF(name_obj);
|
||||
if (module3 == NULL) {
|
||||
goto error;
|
||||
}
|
||||
assert(PyModule_Check(module3));
|
||||
assert(module3 == module);
|
||||
// module3 is a borrowed ref
|
||||
|
||||
return module;
|
||||
|
||||
error:
|
||||
Py_DECREF(module);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static PyMethodDef TestMethods[] = {
|
||||
{"set_errno", set_errno, METH_VARARGS},
|
||||
{"test_config", test_config, METH_NOARGS},
|
||||
|
@ -3468,6 +3515,7 @@ static PyMethodDef TestMethods[] = {
|
|||
{"function_get_kw_defaults", function_get_kw_defaults, METH_O, NULL},
|
||||
{"function_set_kw_defaults", function_set_kw_defaults, METH_VARARGS, NULL},
|
||||
{"test_atexit", test_atexit, METH_NOARGS},
|
||||
{"check_pyimport_addmodule", check_pyimport_addmodule, METH_VARARGS},
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
|
|
|
@ -288,6 +288,7 @@ EXPORT_FUNC(PyGILState_GetThisThreadState)
|
|||
EXPORT_FUNC(PyGILState_Release)
|
||||
EXPORT_FUNC(PyImport_AddModule)
|
||||
EXPORT_FUNC(PyImport_AddModuleObject)
|
||||
EXPORT_FUNC(PyImport_AddModuleRef)
|
||||
EXPORT_FUNC(PyImport_AppendInittab)
|
||||
EXPORT_FUNC(PyImport_ExecCodeModule)
|
||||
EXPORT_FUNC(PyImport_ExecCodeModuleEx)
|
||||
|
|
|
@ -350,20 +350,38 @@ import_add_module(PyThreadState *tstate, PyObject *name)
|
|||
return m;
|
||||
}
|
||||
|
||||
PyObject *
|
||||
PyImport_AddModuleRef(const char *name)
|
||||
{
|
||||
PyObject *name_obj = PyUnicode_FromString(name);
|
||||
if (name_obj == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
PyThreadState *tstate = _PyThreadState_GET();
|
||||
PyObject *module = import_add_module(tstate, name_obj);
|
||||
Py_DECREF(name_obj);
|
||||
return module;
|
||||
}
|
||||
|
||||
|
||||
PyObject *
|
||||
PyImport_AddModuleObject(PyObject *name)
|
||||
{
|
||||
PyThreadState *tstate = _PyThreadState_GET();
|
||||
PyObject *mod = import_add_module(tstate, name);
|
||||
if (mod) {
|
||||
if (!mod) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// gh-86160: PyImport_AddModuleObject() returns a borrowed reference
|
||||
PyObject *ref = PyWeakref_NewRef(mod, NULL);
|
||||
Py_DECREF(mod);
|
||||
if (ref == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mod = PyWeakref_GetObject(ref);
|
||||
Py_DECREF(ref);
|
||||
}
|
||||
return mod; /* borrowed reference */
|
||||
}
|
||||
|
||||
|
@ -2240,11 +2258,12 @@ init_importlib(PyThreadState *tstate, PyObject *sysmod)
|
|||
if (PyImport_ImportFrozenModule("_frozen_importlib") <= 0) {
|
||||
return -1;
|
||||
}
|
||||
PyObject *importlib = PyImport_AddModule("_frozen_importlib"); // borrowed
|
||||
|
||||
PyObject *importlib = PyImport_AddModuleRef("_frozen_importlib");
|
||||
if (importlib == NULL) {
|
||||
return -1;
|
||||
}
|
||||
IMPORTLIB(interp) = Py_NewRef(importlib);
|
||||
IMPORTLIB(interp) = importlib;
|
||||
|
||||
// Import the _imp module
|
||||
if (verbose) {
|
||||
|
|
|
@ -406,7 +406,7 @@ _PyRun_SimpleFileObject(FILE *fp, PyObject *filename, int closeit,
|
|||
{
|
||||
int ret = -1;
|
||||
|
||||
PyObject *main_module = Py_XNewRef(PyImport_AddModule("__main__"));
|
||||
PyObject *main_module = PyImport_AddModuleRef("__main__");
|
||||
if (main_module == NULL)
|
||||
return -1;
|
||||
PyObject *dict = PyModule_GetDict(main_module); // borrowed ref
|
||||
|
@ -502,7 +502,7 @@ PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit,
|
|||
int
|
||||
PyRun_SimpleStringFlags(const char *command, PyCompilerFlags *flags)
|
||||
{
|
||||
PyObject *main_module = Py_XNewRef(PyImport_AddModule("__main__"));
|
||||
PyObject *main_module = PyImport_AddModuleRef("__main__");
|
||||
if (main_module == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue