Skip to content

Commit

Permalink
pythongh-106915: Add PyImport_ImportOrAddModule() function
Browse files Browse the repository at this point in the history
Remove PyImport_AddModuleRef() function.
  • Loading branch information
vstinner committed Nov 7, 2023
1 parent 13405ec commit 2f3cc67
Show file tree
Hide file tree
Showing 13 changed files with 95 additions and 54 deletions.
30 changes: 19 additions & 11 deletions Doc/c-api/import.rst
Original file line number Diff line number Diff line change
Expand Up @@ -98,16 +98,22 @@ Importing Modules
an exception set on failure (the module still exists in this case).
.. c:function:: PyObject* PyImport_AddModuleRef(const char *name)
.. c:function:: int PyImport_ImportOrAddModule(const char *name, PyObject **module)
Return the module object corresponding to a module name.
Create a new module and store it in :data:`sys.modules`, or get an already
imported module from :data:`sys.modules`.
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.
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 *name* argument may be of the form ``package.module``.
- If the module does not exist, create a module, store it in
:data:`sys.modules`, set *\*module* to a :term:`strong reference` to the
module, and return 1.
- If the module was already imported, set *\*module* to a :term:`strong
reference` to the existing module, and return 0.
- On error, raise an exception, set *\*module* to NULL, and return -1.
The module name *name* is decoded from UTF-8.
Expand All @@ -122,16 +128,18 @@ Importing Modules
.. c:function:: PyObject* PyImport_AddModuleObject(PyObject *name)
Similar to :c:func:`PyImport_AddModuleRef`, but return a :term:`borrowed
reference` and *name* is a Python :class:`str` object.
Similar to :c:func:`PyImport_ImportOrAddModule`, but return a :term:`borrowed
reference`, *name* is a Python :class:`str` object, and don't provide the
information if the module was created or was already imported.
.. versionadded:: 3.3
.. c:function:: PyObject* PyImport_AddModule(const char *name)
Similar to :c:func:`PyImport_AddModuleRef`, but return a :term:`borrowed
reference`.
Similar to :c:func:`PyImport_ImportOrAddModule`, but return a :term:`borrowed
reference`, and don't provide the information if the module was created or
was already imported.
.. c:function:: PyObject* PyImport_ExecCodeModule(const char *name, PyObject *co)
Expand Down
3 changes: 0 additions & 3 deletions Doc/data/refcounts.dat
Original file line number Diff line number Diff line change
Expand Up @@ -974,9 +974,6 @@ 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::

Expand Down
2 changes: 1 addition & 1 deletion Doc/data/stable_abi.dat

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions Doc/whatsnew/3.13.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1018,9 +1018,10 @@ 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`.
* Add :c:func:`PyImport_ImportOrAddModule`: similar to
:c:func:`PyImport_AddModule`, but get a :term:`strong reference` instead
of a :term:`borrowed reference` and return 0 if the module was already
imported.
(Contributed by Victor Stinner in :gh:`105922`.)

* Add :c:func:`PyWeakref_GetRef` function: similar to
Expand Down
15 changes: 13 additions & 2 deletions Include/import.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,22 @@ 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 */
// Create a new module and store it in sys.modules, or get an already imported
// module from sys.modules.
//
// - If the module does not exist, create a module, store it in sys.modules,
// set '*module' to a strong reference to the module, and return 1.
// - If the module was already imported, set '*module' to a strong reference
// to the existing module, and return 0.
// - On error, raise an exception, set '*module' to NULL, and return -1.
PyAPI_FUNC(int) PyImport_ImportOrAddModule(
const char *name, // UTF-8 encoded string
PyObject **module
);
#endif

PyAPI_FUNC(PyObject *) PyImport_ImportModule(
const char *name /* UTF-8 encoded string */
);
Expand Down
11 changes: 6 additions & 5 deletions Lib/test/test_import/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2669,24 +2669,25 @@ def test_basic_multiple_interpreters_reset_each(self):
@cpython_only
class CAPITests(unittest.TestCase):
def test_pyimport_addmodule(self):
# gh-105922: Test PyImport_AddModuleRef(), PyImport_AddModule()
# and PyImport_AddModuleObject()
# gh-105922: Test PyImport_ImportOrAddModule(), PyImport_AddModule()
# and PyImport_AddModuleObject(): module already exists.
import _testcapi
for name in (
'sys', # frozen module
'test', # package
__name__, # package.module
):
_testcapi.check_pyimport_addmodule(name)
self.assertIn(name, sys.modules)
_testcapi.check_pyimport_addmodule(name, False)

def test_pyimport_addmodule_create(self):
# gh-105922: Test PyImport_AddModuleRef(), create a new module
# gh-105922: Test PyImport_ImportOrAddModule(): create a new module
import _testcapi
name = 'dontexist'
self.assertNotIn(name, sys.modules)
self.addCleanup(unload, name)

mod = _testcapi.check_pyimport_addmodule(name)
mod = _testcapi.check_pyimport_addmodule(name, True)
self.assertIs(mod, sys.modules[name])


Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_stable_abi_ctypes.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add :c:func:`PyImport_ImportOrAddModule` function and remove
:c:func:`PyImport_AddModuleRef` function. Patch by Victor Stinner.
2 changes: 1 addition & 1 deletion Misc/stable_abi.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2428,7 +2428,7 @@
added = '3.12'
[const.Py_TPFLAGS_ITEMS_AT_END]
added = '3.12'
[function.PyImport_AddModuleRef]
[function.PyImport_ImportOrAddModule]
added = '3.13'
[function.PyWeakref_GetRef]
added = '3.13'
Expand Down
12 changes: 8 additions & 4 deletions Modules/_testcapimodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -3036,15 +3036,19 @@ static PyObject *
check_pyimport_addmodule(PyObject *self, PyObject *args)
{
const char *name;
if (!PyArg_ParseTuple(args, "s", &name)) {
int is_new;
if (!PyArg_ParseTuple(args, "si", &name, &is_new)) {
return NULL;
}
// name must be the name of a module which is already in sys.modules

// test PyImport_AddModuleRef()
PyObject *module = PyImport_AddModuleRef(name);
if (module == NULL) {
// test PyImport_ImportOrAddModule()
PyObject *module = UNINITIALIZED_PTR;
int res = PyImport_ImportOrAddModule(name, &module);
if (res < 0) {
return NULL;
}
assert(res == is_new);
assert(PyModule_Check(module));
// module is a strong reference

Expand Down
2 changes: 1 addition & 1 deletion PC/python3dll.c

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 32 additions & 16 deletions Python/import.c
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ PyImport_GetModule(PyObject *name)
if not, create a new one and insert it in the modules dictionary. */

static PyObject *
import_add_module(PyThreadState *tstate, PyObject *name)
import_add_module(PyThreadState *tstate, PyObject *name, int *is_new)
{
PyObject *modules = MODULES(tstate->interp);
if (modules == NULL) {
Expand All @@ -305,39 +305,55 @@ import_add_module(PyThreadState *tstate, PyObject *name)
return NULL;
}
if (m != NULL && PyModule_Check(m)) {
if (is_new) {
*is_new = 0;
}
return m;
}
Py_XDECREF(m);

m = PyModule_NewObject(name);
if (m == NULL)
if (m == NULL) {
return NULL;
}
if (PyObject_SetItem(modules, name, m) != 0) {
Py_DECREF(m);
return NULL;
}

if (is_new) {
*is_new = 1;
}
return m;
}

PyObject *
PyImport_AddModuleRef(const char *name)
int
PyImport_ImportOrAddModule(const char *name, PyObject **pmodule)
{
PyObject *name_obj = PyUnicode_FromString(name);
if (name_obj == NULL) {
return NULL;
*pmodule = NULL;
return -1;
}
PyThreadState *tstate = _PyThreadState_GET();
PyObject *module = import_add_module(tstate, name_obj);
int is_new;
PyObject *module = import_add_module(tstate, name_obj, &is_new);
Py_DECREF(name_obj);
return module;

*pmodule = module;
if (module == NULL) {
assert(PyErr_Occurred());
return -1;
}
return is_new;
}


PyObject *
PyImport_AddModuleObject(PyObject *name)
{
PyThreadState *tstate = _PyThreadState_GET();
PyObject *mod = import_add_module(tstate, name);
PyObject *mod = import_add_module(tstate, name, NULL);
if (!mod) {
return NULL;
}
Expand All @@ -351,7 +367,7 @@ PyImport_AddModuleObject(PyObject *name)
// unknown. With weakref we can be sure that we get either a reference to
// live object or NULL.
//
// Use PyImport_AddModuleRef() to avoid these issues.
// Use PyImport_ImportOrAddModule() to avoid these issues.
PyObject *ref = PyWeakref_NewRef(mod, NULL);
Py_DECREF(mod);
if (ref == NULL) {
Expand Down Expand Up @@ -1258,7 +1274,7 @@ import_find_extension(PyThreadState *tstate, PyObject *name,
return NULL;
}
}
mod = import_add_module(tstate, name);
mod = import_add_module(tstate, name, NULL);
if (mod == NULL) {
return NULL;
}
Expand Down Expand Up @@ -1386,7 +1402,7 @@ create_builtin(PyThreadState *tstate, PyObject *name, PyObject *spec)
if (_PyUnicode_EqualToASCIIString(name, p->name)) {
if (p->initfunc == NULL) {
/* Cannot re-init internal module ("sys" or "builtins") */
return import_add_module(tstate, name);
return import_add_module(tstate, name, NULL);
}
mod = (*p->initfunc)();
if (mod == NULL) {
Expand Down Expand Up @@ -1671,7 +1687,7 @@ module_dict_for_exec(PyThreadState *tstate, PyObject *name)
{
PyObject *m, *d;

m = import_add_module(tstate, name);
m = import_add_module(tstate, name, NULL);
if (m == NULL)
return NULL;
/* If the module is being reloaded, we get the old module back
Expand Down Expand Up @@ -2110,7 +2126,7 @@ PyImport_ImportFrozenModuleObject(PyObject *name)
if (info.is_package) {
/* Set __path__ to the empty list */
PyObject *l;
m = import_add_module(tstate, name);
m = import_add_module(tstate, name, NULL);
if (m == NULL)
goto err_return;
d = PyModule_GetDict(m);
Expand Down Expand Up @@ -2252,8 +2268,8 @@ init_importlib(PyThreadState *tstate, PyObject *sysmod)
return -1;
}

PyObject *importlib = PyImport_AddModuleRef("_frozen_importlib");
if (importlib == NULL) {
PyObject *importlib;
if (PyImport_ImportOrAddModule("_frozen_importlib", &importlib) < 0) {
return -1;
}
IMPORTLIB(interp) = importlib;
Expand Down Expand Up @@ -3464,7 +3480,7 @@ _imp_init_frozen_impl(PyObject *module, PyObject *name)
if (ret == 0) {
Py_RETURN_NONE;
}
return import_add_module(tstate, name);
return import_add_module(tstate, name, NULL);
}

/*[clinic input]
Expand Down
13 changes: 7 additions & 6 deletions Python/pythonrun.c
Original file line number Diff line number Diff line change
Expand Up @@ -277,8 +277,8 @@ PyRun_InteractiveOneObjectEx(FILE *fp, PyObject *filename,
return parse_res;
}

PyObject *main_module = PyImport_AddModuleRef("__main__");
if (main_module == NULL) {
PyObject *main_module;
if (PyImport_ImportOrAddModule("__main__", &main_module) < 0) {
_PyArena_Free(arena);
return -1;
}
Expand Down Expand Up @@ -407,9 +407,10 @@ _PyRun_SimpleFileObject(FILE *fp, PyObject *filename, int closeit,
{
int ret = -1;

PyObject *main_module = PyImport_AddModuleRef("__main__");
if (main_module == NULL)
PyObject *main_module;
if (PyImport_ImportOrAddModule("__main__", &main_module) < 0) {
return -1;
}
PyObject *dict = PyModule_GetDict(main_module); // borrowed ref

int set_file_name = 0;
Expand Down Expand Up @@ -503,8 +504,8 @@ PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit,

int
_PyRun_SimpleStringFlagsWithName(const char *command, const char* name, PyCompilerFlags *flags) {
PyObject *main_module = PyImport_AddModuleRef("__main__");
if (main_module == NULL) {
PyObject *main_module;
if (PyImport_ImportOrAddModule("__main__", &main_module) < 0) {
return -1;
}
PyObject *dict = PyModule_GetDict(main_module); // borrowed ref
Expand Down

0 comments on commit 2f3cc67

Please sign in to comment.