Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions Lib/test/test_lazy_import/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,22 @@ 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(f"""
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."""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Improve :exc:`AttributeError` messages from unresolved lazy imports. Patch
by Bartosz Sławecki.
23 changes: 23 additions & 0 deletions Objects/lazyimportobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,28 @@ 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()) {
/* Bubble up earlier unrelated 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)
{
Expand Down Expand Up @@ -149,6 +171,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,
Expand Down
Loading