PEP 810 – Explicit lazy imports
Overall reception and motivation
- Many commenters see PEP 810 as one of the cleanest, best‑motivated PEPs in a while: narrowly scoped, explicitly opt‑in, and aimed at a real pain point (slow startup and scattered inline imports).
- Strong interest from people building CLIs, test runners, large apps (e.g. Django) and scientific stacks where imports of heavy dependencies dominate startup time.
Relation to PEP 690 and prior work
- PEP 810 is repeatedly contrasted with the rejected PEP 690:
- 810 is explicit (
lazy import) instead of global/implicit. - Laziness is per‑statement, doesn’t cascade automatically to dependencies.
- Implementation uses proxies instead of deep changes to dictionaries/import machinery.
- 810 is explicit (
- Meta’s Cinder / lazy‑import experience is cited: they got big speedups, but also serious breakage in libraries relying on import‑time side effects (NumPy, SciPy, PyTorch, Dash, etc.).
Side effects, correctness, and late failures
- Major concern: imports do real work at module top level—registration in global registries, monkey‑patching, CLI wiring, etc.—and deferring that can produce subtle, late runtime failures.
- Some argue “fail fast” via eager imports is a feature, especially for long‑lived services.
- Others counter that top‑level side effects are a design smell and that tests plus opt‑in usage mitigate the risk.
- Thread‑safety worries: lazy imports may run at unpredictable times and in arbitrary threads, turning previously “safe at startup” code into Heisenbugs.
CLI startup performance and current workarounds
- Multiple examples of slow imports (e.g. inflect, PyTorch) severely impacting CLI tools, plugin systems, and
pipitself. - Common workaround today: move imports inside functions, sometimes guarded by conditions or try/except; this:
- Duplicates imports across functions.
- Obscures module dependencies.
- Fights linters that demand top‑level imports.
- PEP 810 is seen as a cleaner way to keep imports at the top while deferring cost.
Circular imports and failure timing
- Some hope lazy imports will “solve” circular imports; others worry it will encourage papering over bad architecture.
- Counterpoint: even now, many circular‑import issues can be solved by importing the module rather than
from module import name.
Syntax, defaults, and alternative designs
- Significant bikeshedding over
lazyas a new keyword; alternatives likedeferor decorator‑style statement annotations are proposed. - Debate over default: some want lazy‑by‑default with an
eagerescape hatch; others insist that changing default import semantics would be unacceptably breaking. - Alternative design ideas:
- Modules declaring themselves “lazy‑safe” or “pure” at the top, so importers don’t need
lazy. - Project‑ or interpreter‑level controls (flags, env vars, config) to turn all imports lazy, seen by some as a “break my libraries” mode.
- Modules declaring themselves “lazy‑safe” or “pure” at the top, so importers don’t need
Library vs caller control
- One camp: caller knows best when a dependency is actually needed, so lazy control belongs at the import site.
- Another camp: the module author is best placed to know whether laziness is safe; they point to module‑level
__getattr__and other patterns as existing mechanisms for self‑managed laziness. - Concern that heterogeneous ecosystems (some code assuming lazy, some eager) could force libraries to support both modes, increasing maintenance burden.