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

Skip to content

Commit 3f9eee6

Browse files
bpo-28411: Support other mappings in PyInterpreterState.modules. (#3593)
The concrete PyDict_* API is used to interact with PyInterpreterState.modules in a number of places. This isn't compatible with all dict subclasses, nor with other Mapping implementations. This patch switches the concrete API usage to the corresponding abstract API calls. We also add a PyImport_GetModule() function (and some other helpers) to reduce a bunch of code duplication.
1 parent e82c034 commit 3f9eee6

File tree

11 files changed

+216
-113
lines changed

11 files changed

+216
-113
lines changed

Doc/c-api/import.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,13 @@ Importing Modules
204204
Return the dictionary used for the module administration (a.k.a.
205205
``sys.modules``). Note that this is a per-interpreter variable.
206206
207+
.. c:function:: PyObject* PyImport_GetModule(PyObject *name)
208+
209+
Return the already imported module with the given name. If the
210+
module has not been imported yet then returns NULL but does not set
211+
an error. Returns NULL and sets an error if the lookup failed.
212+
213+
.. versionadded:: 3.7
207214
208215
.. c:function:: PyObject* PyImport_GetImporter(PyObject *path)
209216

Include/import.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,14 @@ PyAPI_FUNC(PyObject *) PyImport_ExecCodeModuleObject(
3838
);
3939
#endif
4040
PyAPI_FUNC(PyObject *) PyImport_GetModuleDict(void);
41+
PyAPI_FUNC(PyObject *) PyImport_GetModule(PyObject *name);
4142
#ifndef Py_LIMITED_API
4243
PyAPI_FUNC(int) _PyImport_IsInitialized(PyInterpreterState *);
44+
PyAPI_FUNC(PyObject *) _PyImport_GetModuleId(struct _Py_Identifier *name);
45+
PyAPI_FUNC(PyObject *) _PyImport_AddModuleObject(PyObject *name,
46+
PyObject *modules);
47+
PyAPI_FUNC(int) _PyImport_SetModule(PyObject *name, PyObject *module);
48+
PyAPI_FUNC(int) _PyImport_SetModuleString(const char *name, PyObject* module);
4349
#endif
4450
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000
4551
PyAPI_FUNC(PyObject *) PyImport_AddModuleObject(
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Switch to the abstract API when dealing with ``PyInterpreterState.modules``.
2+
This allows later support for all dict subclasses and other Mapping
3+
implementations. Also add a ``PyImport_GetModule()`` function to reduce
4+
a bunch of duplicated code.

Modules/_pickle.c

Lines changed: 68 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1649,13 +1649,40 @@ getattribute(PyObject *obj, PyObject *name, int allow_qualname)
16491649
return attr;
16501650
}
16511651

1652+
static int
1653+
_checkmodule(PyObject *module_name, PyObject *module,
1654+
PyObject *global, PyObject *dotted_path)
1655+
{
1656+
if (module == Py_None) {
1657+
return -1;
1658+
}
1659+
if (PyUnicode_Check(module_name) &&
1660+
_PyUnicode_EqualToASCIIString(module_name, "__main__")) {
1661+
return -1;
1662+
}
1663+
1664+
PyObject *candidate = get_deep_attribute(module, dotted_path, NULL);
1665+
if (candidate == NULL) {
1666+
if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
1667+
PyErr_Clear();
1668+
}
1669+
return -1;
1670+
}
1671+
if (candidate != global) {
1672+
Py_DECREF(candidate);
1673+
return -1;
1674+
}
1675+
Py_DECREF(candidate);
1676+
return 0;
1677+
}
1678+
16521679
static PyObject *
16531680
whichmodule(PyObject *global, PyObject *dotted_path)
16541681
{
16551682
PyObject *module_name;
1656-
PyObject *modules_dict;
1657-
PyObject *module;
1683+
PyObject *module = NULL;
16581684
Py_ssize_t i;
1685+
PyObject *modules;
16591686
_Py_IDENTIFIER(__module__);
16601687
_Py_IDENTIFIER(modules);
16611688
_Py_IDENTIFIER(__main__);
@@ -1678,35 +1705,48 @@ whichmodule(PyObject *global, PyObject *dotted_path)
16781705
assert(module_name == NULL);
16791706

16801707
/* Fallback on walking sys.modules */
1681-
modules_dict = _PySys_GetObjectId(&PyId_modules);
1682-
if (modules_dict == NULL) {
1708+
modules = _PySys_GetObjectId(&PyId_modules);
1709+
if (modules == NULL) {
16831710
PyErr_SetString(PyExc_RuntimeError, "unable to get sys.modules");
16841711
return NULL;
16851712
}
1686-
1687-
i = 0;
1688-
while (PyDict_Next(modules_dict, &i, &module_name, &module)) {
1689-
PyObject *candidate;
1690-
if (PyUnicode_Check(module_name) &&
1691-
_PyUnicode_EqualToASCIIString(module_name, "__main__"))
1692-
continue;
1693-
if (module == Py_None)
1694-
continue;
1695-
1696-
candidate = get_deep_attribute(module, dotted_path, NULL);
1697-
if (candidate == NULL) {
1698-
if (!PyErr_ExceptionMatches(PyExc_AttributeError))
1713+
if (PyDict_CheckExact(modules)) {
1714+
i = 0;
1715+
while (PyDict_Next(modules, &i, &module_name, &module)) {
1716+
if (_checkmodule(module_name, module, global, dotted_path) == 0) {
1717+
Py_INCREF(module_name);
1718+
return module_name;
1719+
}
1720+
if (PyErr_Occurred()) {
16991721
return NULL;
1700-
PyErr_Clear();
1701-
continue;
1722+
}
17021723
}
1703-
1704-
if (candidate == global) {
1705-
Py_INCREF(module_name);
1706-
Py_DECREF(candidate);
1707-
return module_name;
1724+
}
1725+
else {
1726+
PyObject *iterator = PyObject_GetIter(modules);
1727+
if (iterator == NULL) {
1728+
return NULL;
17081729
}
1709-
Py_DECREF(candidate);
1730+
while ((module_name = PyIter_Next(iterator))) {
1731+
module = PyObject_GetItem(modules, module_name);
1732+
if (module == NULL) {
1733+
Py_DECREF(module_name);
1734+
Py_DECREF(iterator);
1735+
return NULL;
1736+
}
1737+
if (_checkmodule(module_name, module, global, dotted_path) == 0) {
1738+
Py_DECREF(module);
1739+
Py_DECREF(iterator);
1740+
return module_name;
1741+
}
1742+
Py_DECREF(module);
1743+
Py_DECREF(module_name);
1744+
if (PyErr_Occurred()) {
1745+
Py_DECREF(iterator);
1746+
return NULL;
1747+
}
1748+
}
1749+
Py_DECREF(iterator);
17101750
}
17111751

17121752
/* If no module is found, use __main__. */
@@ -6424,9 +6464,7 @@ _pickle_Unpickler_find_class_impl(UnpicklerObject *self,
64246464
/*[clinic end generated code: output=becc08d7f9ed41e3 input=e2e6a865de093ef4]*/
64256465
{
64266466
PyObject *global;
6427-
PyObject *modules_dict;
64286467
PyObject *module;
6429-
_Py_IDENTIFIER(modules);
64306468

64316469
/* Try to map the old names used in Python 2.x to the new ones used in
64326470
Python 3.x. We do this only with old pickle protocols and when the
@@ -6483,25 +6521,16 @@ _pickle_Unpickler_find_class_impl(UnpicklerObject *self,
64836521
}
64846522
}
64856523

6486-
modules_dict = _PySys_GetObjectId(&PyId_modules);
6487-
if (modules_dict == NULL) {
6488-
PyErr_SetString(PyExc_RuntimeError, "unable to get sys.modules");
6489-
return NULL;
6490-
}
6491-
6492-
module = PyDict_GetItemWithError(modules_dict, module_name);
6524+
module = PyImport_GetModule(module_name);
64936525
if (module == NULL) {
64946526
if (PyErr_Occurred())
64956527
return NULL;
64966528
module = PyImport_Import(module_name);
64976529
if (module == NULL)
64986530
return NULL;
6499-
global = getattribute(module, global_name, self->proto >= 4);
6500-
Py_DECREF(module);
6501-
}
6502-
else {
6503-
global = getattribute(module, global_name, self->proto >= 4);
65046531
}
6532+
global = getattribute(module, global_name, self->proto >= 4);
6533+
Py_DECREF(module);
65056534
return global;
65066535
}
65076536

Modules/pyexpat.c

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1643,7 +1643,6 @@ MODULE_INITFUNC(void)
16431643
PyObject *errors_module;
16441644
PyObject *modelmod_name;
16451645
PyObject *model_module;
1646-
PyObject *sys_modules;
16471646
PyObject *tmpnum, *tmpstr;
16481647
PyObject *codes_dict;
16491648
PyObject *rev_codes_dict;
@@ -1693,11 +1692,6 @@ MODULE_INITFUNC(void)
16931692
*/
16941693
PyModule_AddStringConstant(m, "native_encoding", "UTF-8");
16951694

1696-
sys_modules = PySys_GetObject("modules");
1697-
if (sys_modules == NULL) {
1698-
Py_DECREF(m);
1699-
return NULL;
1700-
}
17011695
d = PyModule_GetDict(m);
17021696
if (d == NULL) {
17031697
Py_DECREF(m);
@@ -1707,7 +1701,7 @@ MODULE_INITFUNC(void)
17071701
if (errors_module == NULL) {
17081702
errors_module = PyModule_New(MODULE_NAME ".errors");
17091703
if (errors_module != NULL) {
1710-
PyDict_SetItem(sys_modules, errmod_name, errors_module);
1704+
_PyImport_SetModule(errmod_name, errors_module);
17111705
/* gives away the reference to errors_module */
17121706
PyModule_AddObject(m, "errors", errors_module);
17131707
}
@@ -1717,7 +1711,7 @@ MODULE_INITFUNC(void)
17171711
if (model_module == NULL) {
17181712
model_module = PyModule_New(MODULE_NAME ".model");
17191713
if (model_module != NULL) {
1720-
PyDict_SetItem(sys_modules, modelmod_name, model_module);
1714+
_PyImport_SetModule(modelmod_name, model_module);
17211715
/* gives away the reference to model_module */
17221716
PyModule_AddObject(m, "model", model_module);
17231717
}

Objects/typeobject.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3913,10 +3913,8 @@ import_copyreg(void)
39133913
by storing a reference to the cached module in a static variable, but
39143914
this broke when multiple embedded interpreters were in use (see issue
39153915
#17408 and #19088). */
3916-
PyObject *modules = PyImport_GetModuleDict();
3917-
copyreg_module = PyDict_GetItemWithError(modules, copyreg_str);
3916+
copyreg_module = PyImport_GetModule(copyreg_str);
39183917
if (copyreg_module != NULL) {
3919-
Py_INCREF(copyreg_module);
39203918
return copyreg_module;
39213919
}
39223920
if (PyErr_Occurred()) {

Python/_warnings.c

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ static PyObject *
3838
get_warnings_attr(const char *attr, int try_import)
3939
{
4040
static PyObject *warnings_str = NULL;
41-
PyObject *all_modules;
4241
PyObject *warnings_module, *obj;
4342

4443
if (warnings_str == NULL) {
@@ -58,13 +57,9 @@ get_warnings_attr(const char *attr, int try_import)
5857
}
5958
}
6059
else {
61-
all_modules = PyImport_GetModuleDict();
62-
63-
warnings_module = PyDict_GetItem(all_modules, warnings_str);
60+
warnings_module = PyImport_GetModule(warnings_str);
6461
if (warnings_module == NULL)
6562
return NULL;
66-
67-
Py_INCREF(warnings_module);
6863
}
6964

7065
if (!PyObject_HasAttrString(warnings_module, attr)) {

Python/ceval.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4935,13 +4935,12 @@ import_from(PyObject *v, PyObject *name)
49354935
Py_DECREF(pkgname);
49364936
return NULL;
49374937
}
4938-
x = PyDict_GetItem(PyImport_GetModuleDict(), fullmodname);
4938+
x = PyImport_GetModule(fullmodname);
49394939
Py_DECREF(fullmodname);
49404940
if (x == NULL) {
49414941
goto error;
49424942
}
49434943
Py_DECREF(pkgname);
4944-
Py_INCREF(x);
49454944
return x;
49464945
error:
49474946
pkgpath = PyModule_GetFilenameObject(v);

0 commit comments

Comments
 (0)