Skip to content

Commit 125ca26

Browse files
authored
gh-154196: Improve AttributeError messages from unresolved lazy imports (#154688)
1 parent 084230e commit 125ca26

3 files changed

Lines changed: 43 additions & 0 deletions

File tree

Lib/test/test_lazy_import/__init__.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,23 @@ def test_lazy_import_type_attributes_accessible(self):
277277
proc = assert_python_ok("-c", code)
278278
self.assertIn(b"<built-in method resolve of lazy_import object at", proc.out)
279279

280+
@support.requires_subprocess()
281+
def test_lazy_import_type_attribute_error_message(self):
282+
"""Check that LazyImportType attribute error message is helpful."""
283+
code = textwrap.dedent("""
284+
lazy import asyncio
285+
try:
286+
globals()["asyncio"].Task
287+
except AttributeError as exc:
288+
assert str(exc) == (
289+
"cannot access attribute 'Task' "
290+
"on unresolved lazy import 'asyncio'"
291+
), repr(str(exc))
292+
else:
293+
assert False, 'AttributeError is not raised'
294+
""")
295+
assert_python_ok("-c", code)
296+
280297

281298
class SyntaxRestrictionTests(LazyImportTestCase):
282299
"""Tests for syntax restrictions on lazy imports."""
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Improve :exc:`AttributeError` messages from unresolved lazy imports. Patch
2+
by Bartosz Sławecki.

Objects/lazyimportobject.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,29 @@ lazy_import_dealloc(PyObject *op)
8181
Py_TYPE(op)->tp_free(op);
8282
}
8383

84+
/* Specialize the error message for failed attribute lookups. */
85+
static PyObject *
86+
lazy_import_getattro(PyObject *op, PyObject *name)
87+
{
88+
PyObject *value = _PyObject_GenericGetAttrWithDict(op, name, NULL, /* suppress */1);
89+
if (value == NULL) {
90+
if (PyErr_Occurred()) {
91+
// pass up non-AttributeError exception
92+
return NULL;
93+
}
94+
PyObject *lz_name = _PyLazyImport_GetName(op);
95+
if (lz_name == NULL) {
96+
return NULL;
97+
}
98+
PyErr_Format(PyExc_AttributeError,
99+
"cannot access attribute %R on unresolved lazy import %R",
100+
name, lz_name);
101+
Py_DECREF(lz_name);
102+
return NULL;
103+
}
104+
return value;
105+
}
106+
84107
static PyObject *
85108
lazy_import_name(PyLazyImportObject *m)
86109
{
@@ -149,6 +172,7 @@ PyTypeObject PyLazyImport_Type = {
149172
.tp_repr = lazy_import_repr,
150173
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
151174
.tp_doc = lazy_import_doc,
175+
.tp_getattro = lazy_import_getattro,
152176
.tp_traverse = lazy_import_traverse,
153177
.tp_clear = lazy_import_clear,
154178
.tp_methods = lazy_import_methods,

0 commit comments

Comments
 (0)