Sys.lazy_modules clarification

Hi,
On the implementation issue for PEP 810 (lazy imports), I asked for a clarification on how sys.lazy_modules should work, and didn’t get a reply. I’m trying here.

cc @Dino @brittanyrey

What are the strings in sys.lazy_modules, exactly?

The PEP (and current docs) say that sys.lazy_modules should be a set of fully qualified module names. This isn’t the case – the interpreter can’t know if something’s a module before it’s imported, and it’s possible to lazy-import non-modules:

>>> import sys
>>> lazy from math import pi
>>> 'math.pi' in sys.lazy_modules   # 'math.pi' is not a *module* name.
True

So, what are the strings in sys.modules? Ideally definition that’s formally correct, but also practically useful – ideally, it would be clear how you’d turn the name into the corresponding imported object.
The format is similar to input to pkgutil.resolve_name – the first, “dots-only” form – except here, only the last identifier can be a non-module object.
How close is that to the design intent?

notes on the fromat

The resolve_name point out the issues of this format, but here they don’t really matter – the information loss mirrors the way lazy import statements already aren’t equivalent to non-lazy ones:

lazy import math.pi
print(math.pi)  # OK

import math.pi  # ModuleNotFoundError
from math import pi  # this one's OK

You still can’t do lazy from itertools.chain import from_iterable though.

Is it expected that some entries can’t never disappear?

The PEP (and current docs) say that “When a lazily imported module is accessed for the first time, its name is removed from this set”. This is pedantically correct, but hides the fact that if a module was previously imported normally, it’s never removed from the set:

# Some startup code (for example, starting the REPL):

...
import typing
...


# Then, somewhere else:

import sys

lazy import typing
print(globals()['typing'])  # prints: <lazy_import 'typing'>

print(typing)  # prints: <module 'typing' ...>
print('typing' in sys.lazy_modules)  # prints: True

Is this expected?

5 Likes

Hi Petr,
Sorry for the delay. sys.lazy_modules is a data structure that contains not yet reified lazy imports.

With respect to your example on math.pi, this is true. Since we elide finding the module until it’s actually needed, we cannot confirm whether the objects in the sys.lazy_modules set represent importing an object or a submodule. We could definitely adjust the wording to make that more clear.

For your second concern, I don’t believe this was intentional.

lazy import math.pi

Yikes, this seems pretty bad to me because it’s such a significant difference from normal imports. And unlike the issues w/ the lingering values in sys.lazy_modules which are more for tooling it’ll be easy for someone to start depending upon the behavior now and then we’d have to break them to fix it.

I filed lazy imports allow `lazy import math.pi` · Issue #155194 · python/cpython · GitHub and have [3.15] gh-155194: Fix not raising on non-module import by DinoV · Pull Request #155188 · python/cpython · GitHub and gh-155194: Fix not raising on non-module import by DinoV · Pull Request #155189 · python/cpython · GitHub to fix it.

1 Like