https://github.com/python/cpython/commit/125ca2699228379c9be80ad8a9d5c3631fcac44f
commit: 125ca2699228379c9be80ad8a9d5c3631fcac44f
branch: main
author: Bartosz SÅ‚awecki <[email protected]>
committer: pablogsal <[email protected]>
date: 2026-08-16T14:25:03+01:00
summary:

gh-154196: Improve `AttributeError` messages from unresolved lazy imports 
(#154688)

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2026-07-25-12-43-42.gh-issue-154196.0rAdob.rst
M Lib/test/test_lazy_import/__init__.py
M Objects/lazyimportobject.c

diff --git a/Lib/test/test_lazy_import/__init__.py 
b/Lib/test/test_lazy_import/__init__.py
index b12e209707a9de3..9147e788d7a81f2 100644
--- a/Lib/test/test_lazy_import/__init__.py
+++ b/Lib/test/test_lazy_import/__init__.py
@@ -277,6 +277,23 @@ def test_lazy_import_type_attributes_accessible(self):
         proc = assert_python_ok("-c", code)
         self.assertIn(b"<built-in method resolve of lazy_import object at", 
proc.out)
 
+    @support.requires_subprocess()
+    def test_lazy_import_type_attribute_error_message(self):
+        """Check that LazyImportType attribute error message is helpful."""
+        code = textwrap.dedent("""
+            lazy import asyncio
+            try:
+                globals()["asyncio"].Task
+            except AttributeError as exc:
+                assert str(exc) == (
+                    "cannot access attribute 'Task' "
+                    "on unresolved lazy import 'asyncio'"
+                ), repr(str(exc))
+            else:
+                assert False, 'AttributeError is not raised'
+        """)
+        assert_python_ok("-c", code)
+
 
 class SyntaxRestrictionTests(LazyImportTestCase):
     """Tests for syntax restrictions on lazy imports."""
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2026-07-25-12-43-42.gh-issue-154196.0rAdob.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-25-12-43-42.gh-issue-154196.0rAdob.rst
new file mode 100644
index 000000000000000..8f55e76a5c0dcd4
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-25-12-43-42.gh-issue-154196.0rAdob.rst
@@ -0,0 +1,2 @@
+Improve :exc:`AttributeError` messages from unresolved lazy imports. Patch
+by Bartosz Sławecki.
diff --git a/Objects/lazyimportobject.c b/Objects/lazyimportobject.c
index fa1eb25047d9617..8f7f3f98c291289 100644
--- a/Objects/lazyimportobject.c
+++ b/Objects/lazyimportobject.c
@@ -81,6 +81,29 @@ lazy_import_dealloc(PyObject *op)
     Py_TYPE(op)->tp_free(op);
 }
 
+/* Specialize the error message for failed attribute lookups. */
+static PyObject *
+lazy_import_getattro(PyObject *op, PyObject *name)
+{
+    PyObject *value = _PyObject_GenericGetAttrWithDict(op, name, NULL, /* 
suppress */1);
+    if (value == NULL) {
+        if (PyErr_Occurred()) {
+            // pass up non-AttributeError exception
+            return NULL;
+        }
+        PyObject *lz_name = _PyLazyImport_GetName(op);
+        if (lz_name == NULL) {
+            return NULL;
+        }
+        PyErr_Format(PyExc_AttributeError,
+                     "cannot access attribute %R on unresolved lazy import %R",
+                     name, lz_name);
+        Py_DECREF(lz_name);
+        return NULL;
+    }
+    return value;
+}
+
 static PyObject *
 lazy_import_name(PyLazyImportObject *m)
 {
@@ -149,6 +172,7 @@ PyTypeObject PyLazyImport_Type = {
     .tp_repr = lazy_import_repr,
     .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
     .tp_doc = lazy_import_doc,
+    .tp_getattro = lazy_import_getattro,
     .tp_traverse = lazy_import_traverse,
     .tp_clear = lazy_import_clear,
     .tp_methods = lazy_import_methods,

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to