Skip to content

Commit 543527b

Browse files
committed
gh-148588: Document __lazy_modules__ compatibility mode for lazy imports
Add documentation for the __lazy_modules__ module attribute in two places: - Doc/reference/simple_stmts.rst: new subsection under the lazy imports section explaining the compatibility mode, its semantics (module-scope only, overridden by -X lazy_imports=none), and a usage example. - Doc/reference/datamodel.rst: new module.__lazy_modules__ attribute entry with a cross-reference to the detailed explanation in simple_stmts. Also extend the What's New in Python 3.15 lazy-imports entry with a short paragraph and example demonstrating __lazy_modules__.
1 parent eb4c78d commit 543527b

4 files changed

Lines changed: 68 additions & 0 deletions

File tree

Doc/reference/datamodel.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -926,6 +926,7 @@ Attribute assignment updates the module's namespace dictionary, e.g.,
926926
single: __doc__ (module attribute)
927927
single: __annotations__ (module attribute)
928928
single: __annotate__ (module attribute)
929+
single: __lazy_modules__ (module attribute)
929930
pair: module; namespace
930931

931932
.. _import-mod-attrs:
@@ -1121,6 +1122,19 @@ the following writable attributes:
11211122

11221123
.. versionadded:: 3.14
11231124

1125+
.. attribute:: module.__lazy_modules__
1126+
1127+
An optional sequence of absolute module name strings. When defined at
1128+
module scope, any regular :keyword:`import` statement in that module whose
1129+
target module name appears in this sequence is treated as a
1130+
:ref:`lazy import <lazy-imports>`, as if the :keyword:`lazy` keyword had
1131+
been used. Imports inside functions, class bodies, or
1132+
:keyword:`try`/:keyword:`except`/:keyword:`finally` blocks are unaffected.
1133+
1134+
See :ref:`lazy-modules-compat` for details and examples.
1135+
1136+
.. versionadded:: 3.15
1137+
11241138
Module dictionaries
11251139
^^^^^^^^^^^^^^^^^^^
11261140

Doc/reference/simple_stmts.rst

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -920,6 +920,46 @@ See :pep:`810` for the full specification of lazy imports.
920920

921921
.. versionadded:: 3.15
922922

923+
.. _lazy-modules-compat:
924+
925+
Compatibility mode via ``__lazy_modules__``
926+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
927+
928+
.. index::
929+
single: __lazy_modules__
930+
931+
As an alternative to using the :keyword:`lazy` keyword, a module can opt
932+
into lazy loading for specific imports by defining a module-level
933+
:attr:`~module.__lazy_modules__` variable. When present, it must be a
934+
sequence of absolute module name strings. Any regular (non-``lazy``)
935+
:keyword:`import` statement at module scope whose target appears in
936+
:attr:`!__lazy_modules__` is treated as a lazy import, exactly as if the
937+
:keyword:`lazy` keyword had been used.
938+
939+
This provides a way to enable lazy loading for specific dependencies without
940+
changing individual ``import`` statements — useful when migrating existing
941+
code or when the imports are generated programmatically::
942+
943+
__lazy_modules__ = ["json", "pathlib"]
944+
945+
import json # loaded lazily (name is in __lazy_modules__)
946+
import os # loaded eagerly (name not in __lazy_modules__)
947+
948+
import pathlib # loaded lazily
949+
950+
Relative imports are resolved to their absolute name before the lookup, so
951+
the sequence must always contain absolute module names.
952+
953+
Imports inside functions, class bodies, or
954+
:keyword:`try`/:keyword:`except`/:keyword:`finally` blocks are always eager,
955+
regardless of :attr:`!__lazy_modules__`.
956+
957+
Setting ``-X lazy_imports=none`` (or the :envvar:`PYTHON_LAZY_IMPORTS`
958+
environment variable to ``none``) overrides :attr:`!__lazy_modules__` and
959+
forces all imports to be eager.
960+
961+
.. versionadded:: 3.15
962+
923963
.. _future:
924964

925965
Future statements

Doc/whatsnew/3.15.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,17 @@ function, class body, or ``try``/``except``/``finally`` block raises a
182182
(``lazy from module import *`` and ``lazy from __future__ import ...`` both
183183
raise :exc:`SyntaxError`).
184184

185+
For code that cannot use the ``lazy`` keyword directly — for example, when
186+
migrating a large existing codebase — a module can define
187+
:attr:`~module.__lazy_modules__` as a sequence of absolute module name
188+
strings. Regular ``import`` statements for those modules are then treated
189+
as lazy, with the same semantics as the ``lazy`` keyword::
190+
191+
__lazy_modules__ = ["json", "pathlib"]
192+
193+
import json # lazy
194+
import os # still eager
195+
185196
.. seealso:: :pep:`810` for the full specification and rationale.
186197

187198
(Contributed by Pablo Galindo Salgado and Dino Viehland in :gh:`142349`.)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Document :attr:`module.__lazy_modules__`, the compatibility-mode mechanism
2+
for opting specific imports into lazy loading without the :keyword:`lazy`
3+
keyword, in the language reference and the "What's New in Python 3.15" page.

0 commit comments

Comments
 (0)