Bug report
Bug description:
Accessing a pending lazy submodule through the parent module can hide the real
exception raised while importing the submodule.
Reproducer:
mkdir -p lel/lol printf 'print("LEL")\n' > lel/__init__.py printf '1/0\n' > lel/lol/__init__.py ./python -c 'lazy import lel.lol; lel.lol'
Actual output:
Traceback (most recent call last): File "<string>", line 1, in <module> lazy import lel.lol; lel.lol ^^^^^^^ AttributeError: module 'lel' has no attribute 'lol' LEL
The eager import is much easier to debug:
./python -c 'import lel.lol; lel.lol'Traceback (most recent call last): File "<string>", line 1, in <module> import lel.lol; lel.lol ^^^^^^^^^^^^^^ File "/tmp/lazy-issue-repro/lel/lol/__init__.py", line 1, in <module> 1/0 ~^~ ZeroDivisionError: division by zero LEL
I would expect the lazy import path to propagate the exception raised while
executing lel.lol, probably with the lazy-import reification chain, instead
of turning it into a missing attribute on lel.
Unless I am missing something, this is coming from
Objects/moduleobject.c:try_load_lazy_submodule(). It calls
_PyImport_TryLoadLazySubmodule(), and if that returns NULL, it clears the
active exception:
PyObject *result = _PyImport_TryLoadLazySubmodule(mod_name, name); Py_DECREF(mod_name); if (result == NULL) { PyErr_Clear(); return NULL; }
But _PyImport_TryLoadLazySubmodule() calls PyImport_ImportModuleLevelObject(),
so NULL can mean that the pending submodule exists but failed while importing,
not only that the attribute should fall through to normal AttributeError
handling.
This seems related to gh-150052 / gh-150744, but it looks different from
gh-151208: there is no parent attribute shadowing here, the child module itself
raises during import.
CPython versions tested on:
CPython main branch
Tested on a local 3.16.0a0 main-derived build at 8b59df84c0d, containing
5a2d2736a655d993934adee1bbb568067d53f6f4.
Operating systems tested on:
Linux