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]