Skip to content

Commit e85e79b

Browse files
johnslavikmiss-islington
authored andcommitted
gh-154196: Improve AttributeError messages from unresolved lazy imports (GH-154688)
(cherry picked from commit 125ca26) Co-authored-by: Bartosz Sławecki <bartosz@ilikepython.com>
1 parent 39a0217 commit e85e79b

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
@@ -285,6 +285,23 @@ def test_lazy_import_type_attributes_accessible(self):
285285
proc = assert_python_ok("-c", code)
286286
self.assertIn(b"<built-in method resolve of lazy_import object at", proc.out)
287287

288+
@support.requires_subprocess()
289+
def test_lazy_import_type_attribute_error_message(self):
290+
"""Check that LazyImportType attribute error message is helpful."""
291+
code = textwrap.dedent("""
292+
lazy import asyncio
293+
try:
294+
globals()["asyncio"].Task
295+
except AttributeError as exc:
296+
assert str(exc) == (
297+
"cannot access attribute 'Task' "
298+
"on unresolved lazy import 'asyncio'"
299+
), repr(str(exc))
300+
else:
301+
assert False, 'AttributeError is not raised'
302+
""")
303+
assert_python_ok("-c", code)
304+
288305

289306
class SyntaxRestrictionTests(LazyImportTestCase):
290307
"""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)