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."""
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."""
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) {