mirror of
https://github.com/python/cpython.git
synced 2026-06-05 01:10:53 +00:00
gh-144957: Fix lazy imports + module __getattr__ (GH-149624)
This commit is contained in:
parent
ef877318a0
commit
56171da341
5 changed files with 61 additions and 0 deletions
|
|
@ -88,6 +88,26 @@ def test_basic_used(self):
|
|||
import test.test_lazy_import.data.basic_used
|
||||
self.assertIn("test.test_lazy_import.data.basic2", sys.modules)
|
||||
|
||||
@support.requires_subprocess()
|
||||
def test_from_import_with_module_getattr(self):
|
||||
"""Lazy from import should respect module-level __getattr__."""
|
||||
code = textwrap.dedent("""
|
||||
lazy from test.test_lazy_import.data.module_with_getattr import dynamic_attr
|
||||
assert dynamic_attr == "from_getattr"
|
||||
""")
|
||||
assert_python_ok("-c", code)
|
||||
|
||||
@support.requires_subprocess()
|
||||
def test_from_import_with_imported_module_getattr(self):
|
||||
"""Lazy from import should not shadow an imported module's __getattr__."""
|
||||
code = textwrap.dedent("""
|
||||
import test.test_lazy_import.data.module_with_getattr as mod
|
||||
lazy from test.test_lazy_import.data.module_with_getattr import dynamic_attr
|
||||
assert dynamic_attr == "from_getattr"
|
||||
assert mod.dynamic_attr == "from_getattr"
|
||||
""")
|
||||
assert_python_ok("-c", code)
|
||||
|
||||
|
||||
class GlobalLazyImportModeTests(unittest.TestCase):
|
||||
"""Tests for sys.set_lazy_imports() global mode control."""
|
||||
|
|
@ -385,6 +405,17 @@ def test_lazy_import_pkg_cross_import(self):
|
|||
self.assertEqual(type(g["x"]), int)
|
||||
self.assertEqual(type(g["b"]), types.LazyImportType)
|
||||
|
||||
@support.requires_subprocess()
|
||||
def test_package_from_import_with_module_getattr(self):
|
||||
"""Lazy from import should respect a package's __getattr__."""
|
||||
code = textwrap.dedent("""
|
||||
import test.test_lazy_import.data.pkg as pkg
|
||||
lazy from test.test_lazy_import.data.pkg import dynamic_attr
|
||||
assert dynamic_attr == "from_getattr"
|
||||
assert pkg.dynamic_attr == "from_getattr"
|
||||
""")
|
||||
assert_python_ok("-c", code)
|
||||
|
||||
|
||||
class DunderLazyImportTests(unittest.TestCase):
|
||||
"""Tests for __lazy_import__ builtin function."""
|
||||
|
|
|
|||
4
Lib/test/test_lazy_import/data/module_with_getattr.py
Normal file
4
Lib/test/test_lazy_import/data/module_with_getattr.py
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
def __getattr__(name):
|
||||
if name == "dynamic_attr":
|
||||
return "from_getattr"
|
||||
raise AttributeError(name)
|
||||
|
|
@ -1 +1,6 @@
|
|||
x = 42
|
||||
|
||||
def __getattr__(name):
|
||||
if name == "dynamic_attr":
|
||||
return "from_getattr"
|
||||
raise AttributeError(name)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
Fix lazy ``from`` imports of module attributes provided by module-level
|
||||
``__getattr__``.
|
||||
|
|
@ -1307,6 +1307,25 @@ _Py_module_getattro_impl(PyModuleObject *m, PyObject *name, int suppress)
|
|||
attr = _PyObject_GenericGetAttrWithDict((PyObject *)m, name, NULL, suppress);
|
||||
if (attr) {
|
||||
if (PyLazyImport_CheckExact(attr)) {
|
||||
// gh-144957: Module __getattr__ should get a chance to provide
|
||||
// the attribute before resolving a lazy import placeholder.
|
||||
if (PyDict_GetItemRef(m->md_dict, &_Py_ID(__getattr__), &getattr) < 0) {
|
||||
Py_DECREF(attr);
|
||||
return NULL;
|
||||
}
|
||||
if (getattr) {
|
||||
PyObject *result = PyObject_CallOneArg(getattr, name);
|
||||
Py_DECREF(getattr);
|
||||
if (result != NULL) {
|
||||
Py_DECREF(attr);
|
||||
return result;
|
||||
}
|
||||
if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
|
||||
Py_DECREF(attr);
|
||||
return NULL;
|
||||
}
|
||||
PyErr_Clear();
|
||||
}
|
||||
PyObject *new_value = _PyImport_LoadLazyImportTstate(
|
||||
PyThreadState_GET(), attr);
|
||||
if (new_value == NULL) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue