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
Linked PRs
Bug report
Bug description:
Accessing a pending lazy submodule through the parent module can hide the real
exception raised while importing the submodule.
Reproducer:
Actual output:
The eager import is much easier to debug:
./python -c 'import lel.lol; lel.lol'I would expect the lazy import path to propagate the exception raised while
executing
lel.lol, probably with the lazy-import reification chain, insteadof 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 returnsNULL, it clears theactive exception:
But
_PyImport_TryLoadLazySubmodule()callsPyImport_ImportModuleLevelObject(),so
NULLcan mean that the pending submodule exists but failed while importing,not only that the attribute should fall through to normal
AttributeErrorhandling.
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.0a0main-derived build at8b59df84c0d, containing5a2d2736a655d993934adee1bbb568067d53f6f4.Operating systems tested on:
Linux
Linked PRs