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.
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?