@@ -65,6 +65,8 @@ Summary -- Release highlights
6565
6666.. PEP-sized items next.
6767
68+ * :pep: `810 `: :ref: `Explicit lazy imports for faster startup times
69+ <whatsnew315-pep810>`
6870* :pep: `799 `: :ref: `A dedicated profiling package for organizing Python
6971 profiling tools <whatsnew315-sampling-profiler>`
7072* :pep: `686 `: :ref: `Python now uses UTF-8 as the default encoding
@@ -77,6 +79,97 @@ Summary -- Release highlights
7779New features
7880============
7981
82+ .. _whatsnew315-pep810 :
83+
84+ :pep: `810 `: Explicit lazy imports
85+ ---------------------------------
86+
87+ Large Python applications often suffer from slow startup times. A significant
88+ contributor to this problem is the import system: when a module is imported,
89+ Python must locate the file, read it from disk, compile it to bytecode, and
90+ execute all top-level code. For applications with deep dependency trees, this
91+ process can take seconds, even when most of the imported code is never actually
92+ used during a particular run.
93+
94+ Developers have worked around this by moving imports inside functions, using
95+ ``importlib `` to load modules on demand, or restructuring code to avoid
96+ unnecessary dependencies. These approaches work but make code harder to read
97+ and maintain, scatter import statements throughout the codebase, and require
98+ discipline to apply consistently.
99+
100+ Python now provides a cleaner solution through explicit lazy imports using the
101+ new ``lazy `` soft keyword. When you mark an import as lazy, Python defers the
102+ actual module loading until the imported name is first used. This gives you
103+ the organizational benefits of declaring all imports at the top of the file
104+ while only paying the loading cost for modules you actually use.
105+
106+ The ``lazy `` keyword works with both ``import `` and ``from ... import `` statements.
107+ When you write ``lazy import heavy_module ``, Python does not immediately load the
108+ module. Instead, it creates a lightweight proxy object. The actual module loading
109+ happens transparently when you first access the name:
110+
111+ .. code-block :: python
112+
113+ lazy import json
114+ lazy from datetime import datetime
115+
116+ print (" Starting up..." ) # json and datetime not loaded yet
117+
118+ data = json.loads(' {"key": "value"}' ) # json loads here
119+ now = datetime() # datetime loads here
120+
121+ This mechanism is particularly useful for applications that import many modules
122+ at the top level but may only use a subset of them in any given run. The deferred
123+ loading reduces startup latency without requiring code restructuring or conditional
124+ imports scattered throughout the codebase.
125+
126+ When a lazy import eventually fails (for example, if the module does not exist),
127+ Python raises the exception at the point of first use rather than at import time.
128+ The traceback includes both the location where the name was accessed and the
129+ original import statement, making it straightforward to diagnose the problem.
130+
131+ For cases where you want to enable lazy loading globally without modifying source
132+ code, Python provides the :option: `-X lazy_imports <-X> ` command-line option and
133+ the :envvar: `PYTHON_LAZY_IMPORTS ` environment variable. Both accept three values:
134+ ``all `` makes all imports lazy by default, ``none `` disables lazy imports entirely
135+ (even explicit ``lazy `` statements become eager), and ``normal `` (the default)
136+ respects the ``lazy `` keyword in source code. The :func: `sys.set_lazy_imports ` and
137+ :func: `sys.get_lazy_imports ` functions allow changing and querying this mode at
138+ runtime.
139+
140+ For more selective control, :func: `sys.set_lazy_imports_filter ` accepts a callable
141+ that determines whether a specific module should be loaded lazily. The filter
142+ receives the fully-qualified module name and returns a boolean. This allows
143+ patterns like making only your own application's modules lazy while keeping
144+ third-party dependencies eager:
145+
146+ .. code-block :: python
147+
148+ import sys
149+
150+ sys.set_lazy_imports_filter(lambda name : name.startswith(" myapp." ))
151+ sys.set_lazy_imports(" all" )
152+
153+ import myapp.slow_module # lazy (matches filter)
154+ import json # eager (does not match filter)
155+
156+ For debugging and introspection, :func: `sys.get_lazy_modules ` returns a set
157+ containing the names of all modules that have been lazily imported but not yet
158+ loaded. The proxy type itself is available as :data: `types.LazyImportType ` for
159+ code that needs to detect lazy imports programmatically.
160+
161+ There are some restrictions on where ``lazy `` can appear. Lazy imports are only
162+ permitted at module scope; using ``lazy `` inside a function, class body, or
163+ ``try ``/``except ``/``finally `` block raises a :exc: `SyntaxError `. Star imports
164+ cannot be lazy (``lazy from module import * `` is a syntax error), and future
165+ imports cannot be lazy either (``lazy from __future__ import ... `` raises
166+ :exc: `SyntaxError `).
167+
168+ .. seealso :: :pep:`810` for the full specification and rationale.
169+
170+ (Contributed by Pablo Galindo Salgado and Dino Viehland.)
171+
172+
80173.. _whatsnew315-sampling-profiler :
81174
82175:pep: `799 `: High frequency statistical sampling profiler
0 commit comments