Skip to content

Lazy submodule imports hide exceptions raised while loading the submodule #153236

Description

@pablogsal

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions