diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py index 14a688d..b8cbabc 100644 --- a/Lib/test/test_import/__init__.py +++ b/Lib/test/test_import/__init__.py @@ -1125,6 +1125,12 @@ class CircularImportTests(unittest.TestCase): from test.test_import.data.circular_imports.subpkg import util self.assertIs(util.util, rebinding.util) + def test_wildcard(self): + try: + import test.test_import.data.circular_imports.wildcard + except (ImportError, AttributeError): + self.fail('circular import with a wildcard failed') + if __name__ == '__main__': # Test needs to be a package, so we can do relative imports. diff --git a/Lib/test/test_import/data/circular_imports/__init__.py b/Lib/test/test_import/data/circular_imports/__init__.py new file mode 100644 index 0000000..be491c0 --- /dev/null +++ b/Lib/test/test_import/data/circular_imports/__init__.py @@ -0,0 +1 @@ +__all__ = ['wildcard'] diff --git a/Lib/test/test_import/data/circular_imports/wildcard.py b/Lib/test/test_import/data/circular_imports/wildcard.py new file mode 100644 index 0000000..4f18bc4 --- /dev/null +++ b/Lib/test/test_import/data/circular_imports/wildcard.py @@ -0,0 +1,2 @@ +"""Circular imports with from pkg import *""" +from . import * diff --git a/Python/ceval.c b/Python/ceval.c index 8d2cdc2..a3b73bc 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -5110,8 +5110,10 @@ import_all_from(PyObject *locals, PyObject *v) { _Py_IDENTIFIER(__all__); _Py_IDENTIFIER(__dict__); + _Py_IDENTIFIER(__name__); PyObject *all = _PyObject_GetAttrId(v, &PyId___all__); PyObject *dict, *name, *value; + PyObject *fullmodname, *pkgname; int skip_leading_underscores = 0; int pos, err; @@ -5152,8 +5154,32 @@ import_all_from(PyObject *locals, PyObject *v) continue; } value = PyObject_GetAttr(v, name); - if (value == NULL) + if (value == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) + { + /* Issue #23477: if a circular import happens within a wildcard + import, some partially loaded modules may not have been saved + to the package's __dict__ yet, so we'll try getting them from + sys.modules before giving up. + */ + PyErr_Clear(); + pkgname = _PyObject_GetAttrId(v, &PyId___name__); + if (pkgname != NULL && PyUnicode_Check(pkgname)) + { + fullmodname = PyUnicode_FromFormat("%U.%U", pkgname, name); + if (fullmodname != NULL) + { + value = PyDict_GetItem(PyImport_GetModuleDict(), + fullmodname); + Py_XINCREF(value); + Py_DECREF(fullmodname); + } + Py_DECREF(pkgname); + } + } + if (value == NULL) { + PyErr_Format(PyExc_ImportError, "cannot import name %R", name); err = -1; + } else if (PyDict_CheckExact(locals)) err = PyDict_SetItem(locals, name, value); else