summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/c-api/import.rst37
-rw-r--r--Doc/data/refcounts.dat3
-rw-r--r--Doc/data/stable_abi.dat1
-rw-r--r--Doc/whatsnew/3.13.rst5
-rw-r--r--Include/import.h5
-rw-r--r--Lib/test/test_import/__init__.py24
-rw-r--r--Lib/test/test_stable_abi_ctypes.py1
-rw-r--r--Misc/NEWS.d/next/C API/2023-06-19-20-02-16.gh-issue-105922.o4T6wO.rst3
-rw-r--r--Misc/stable_abi.toml2
-rw-r--r--Modules/_testcapimodule.c48
-rwxr-xr-xPC/python3dll.c1
-rw-r--r--Python/import.c39
-rw-r--r--Python/pythonrun.c4
13 files changed, 149 insertions, 24 deletions
diff --git a/Doc/c-api/import.rst b/Doc/c-api/import.rst
index 6db2023..7aacc21 100644
--- a/Doc/c-api/import.rst
+++ b/Doc/c-api/import.rst
@@ -98,27 +98,40 @@ Importing Modules
an exception set on failure (the module still exists in this case).
-.. c:function:: PyObject* PyImport_AddModuleObject(PyObject *name)
+.. 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.
- 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.
+ The module name *name* is decoded from UTF-8.
- .. 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.
+
+ .. versionadded:: 3.13
+
+
+.. c:function:: PyObject* PyImport_AddModuleObject(PyObject *name)
- 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)
diff --git a/Doc/data/refcounts.dat b/Doc/data/refcounts.dat
index f5628ab..d707cc3 100644
--- a/Doc/data/refcounts.dat
+++ b/Doc/data/refcounts.dat
@@ -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::
diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat
index 80806ae..a3fde01 100644
--- a/Doc/data/stable_abi.dat
+++ b/Doc/data/stable_abi.dat
@@ -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,,
diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst
index 735715f..bbe02a9 100644
--- a/Doc/whatsnew/3.13.rst
+++ b/Doc/whatsnew/3.13.rst
@@ -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
----------------------
diff --git a/Include/import.h b/Include/import.h
index 6c63744..24b23b9 100644
--- a/Include/import.h
+++ b/Include/import.h
@@ -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 */
);
diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py
index 71a50bc..f2726da 100644
--- a/Lib/test/test_import/__init__.py
+++ b/Lib/test/test_import/__init__.py
@@ -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()
diff --git a/Lib/test/test_stable_abi_ctypes.py b/Lib/test/test_stable_abi_ctypes.py
index 8cad71c..c26dff5 100644
--- a/Lib/test/test_stable_abi_ctypes.py
+++ b/Lib/test/test_stable_abi_ctypes.py
@@ -328,6 +328,7 @@ SYMBOL_NAMES = (
"PyGetSetDescr_Type",
"PyImport_AddModule",
"PyImport_AddModuleObject",
+ "PyImport_AddModuleRef",
"PyImport_AppendInittab",
"PyImport_ExecCodeModule",
"PyImport_ExecCodeModuleEx",
diff --git a/Misc/NEWS.d/next/C API/2023-06-19-20-02-16.gh-issue-105922.o4T6wO.rst b/Misc/NEWS.d/next/C API/2023-06-19-20-02-16.gh-issue-105922.o4T6wO.rst
new file mode 100644
index 0000000..7515d68
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2023-06-19-20-02-16.gh-issue-105922.o4T6wO.rst
@@ -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.
diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml
index f910005..7025ed4 100644
--- a/Misc/stable_abi.toml
+++ b/Misc/stable_abi.toml
@@ -2428,3 +2428,5 @@
added = '3.12'
[const.Py_TPFLAGS_ITEMS_AT_END]
added = '3.12'
+[function.PyImport_AddModuleRef]
+ added = '3.13'
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index 35687b4..9d4517c 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -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 */
};
diff --git a/PC/python3dll.c b/PC/python3dll.c
index 505feef..fea19b7 100755
--- a/PC/python3dll.c
+++ b/PC/python3dll.c
@@ -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)
diff --git a/Python/import.c b/Python/import.c
index 779af55..969902a 100644
--- a/Python/import.c
+++ b/Python/import.c
@@ -351,19 +351,37 @@ import_add_module(PyThreadState *tstate, PyObject *name)
}
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) {
- PyObject *ref = PyWeakref_NewRef(mod, NULL);
- Py_DECREF(mod);
- if (ref == NULL) {
- return NULL;
- }
- mod = PyWeakref_GetObject(ref);
- Py_DECREF(ref);
+ 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) {
diff --git a/Python/pythonrun.c b/Python/pythonrun.c
index f9e798e..af82fa4 100644
--- a/Python/pythonrun.c
+++ b/Python/pythonrun.c
@@ -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;
}