https://github.com/python/cpython/commit/9dab866f9ca564a8b9c73393c1a2b1139583d018
commit: 9dab866f9ca564a8b9c73393c1a2b1139583d018
branch: main
author: Bartosz SÅ‚awecki <[email protected]>
committer: pablogsal <[email protected]>
date: 2026-04-25T15:23:40+01:00
summary:

gh-148588: Document `__lazy_modules__` (#148590)

files:
M Doc/reference/datamodel.rst
M Doc/reference/simple_stmts.rst
M Doc/whatsnew/3.15.rst
M Misc/NEWS.d/3.15.0a8.rst

diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst
index 1e53c0e0e6f971..2089984404cec6 100644
--- a/Doc/reference/datamodel.rst
+++ b/Doc/reference/datamodel.rst
@@ -926,6 +926,7 @@ Attribute assignment updates the module's namespace 
dictionary, e.g.,
    single: __doc__ (module attribute)
    single: __annotations__ (module attribute)
    single: __annotate__ (module attribute)
+   single: __lazy_modules__ (module attribute)
    pair: module; namespace
 
 .. _import-mod-attrs:
@@ -1121,6 +1122,20 @@ the following writable attributes:
 
    .. versionadded:: 3.14
 
+.. attribute:: module.__lazy_modules__
+
+   A container (an object implementing :meth:`~object.__contains__`) of fully
+   qualified module name strings.  When defined
+   at module scope, any regular :keyword:`import` statement in that module 
whose
+   target module name appears in this container is treated as a
+   :ref:`lazy import <lazy-imports>`, as if the :keyword:`lazy` keyword had
+   been used.  Imports inside functions, class bodies, or
+   :keyword:`try`/:keyword:`except`/:keyword:`finally` blocks are unaffected.
+
+   See :ref:`lazy-modules-compat` for details and examples.
+
+   .. versionadded:: 3.15
+
 Module dictionaries
 ^^^^^^^^^^^^^^^^^^^
 
diff --git a/Doc/reference/simple_stmts.rst b/Doc/reference/simple_stmts.rst
index 9b84c2e9ac7017..648e3a9bf54060 100644
--- a/Doc/reference/simple_stmts.rst
+++ b/Doc/reference/simple_stmts.rst
@@ -920,6 +920,56 @@ See :pep:`810` for the full specification of lazy imports.
 
 .. versionadded:: 3.15
 
+.. _lazy-modules-compat:
+
+Compatibility via ``__lazy_modules__``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. index::
+   single: __lazy_modules__
+
+As an alternative to using the :keyword:`lazy` keyword, a module can opt
+into lazy loading for specific imports by defining a module-level
+:attr:`~module.__lazy_modules__` variable.  When present, it must be a
+container of fully qualified module name strings.  Any regular (non-``lazy``)
+:keyword:`import` statement at module scope whose target appears in
+:attr:`!__lazy_modules__` is treated as a lazy import, exactly as if the
+:keyword:`lazy` keyword had been used.
+
+This provides a way to enable lazy loading for specific dependencies without
+changing individual ``import`` statements. This is useful when supporting
+Python versions older than 3.15 while using lazy imports in 3.15+::
+
+   __lazy_modules__ = ["json", "pathlib"]
+
+   import json     # loaded lazily (name is in __lazy_modules__)
+   import os       # loaded eagerly (name not in __lazy_modules__)
+
+   import pathlib  # loaded lazily
+
+Relative imports are resolved to their absolute name before the lookup, so
+:attr:`!__lazy_modules__` must always contain fully qualified module names.
+
+For ``from``-style imports, the relevant name is the module following
+``from``, not the names of its members::
+
+   # In mypackage/mymodule.py
+   __lazy_modules__ = ["mypackage", "mypackage.sub.utils"]
+
+   from . import helper         # loaded lazily: . resolves to mypackage
+   from .sub.utils import func  # loaded lazily: .sub.utils resolves to 
mypackage.sub.utils
+   import json                  # loaded eagerly (not in __lazy_modules__)
+
+Imports inside functions, class bodies, or
+:keyword:`try`/:keyword:`except`/:keyword:`finally` blocks are always eager,
+regardless of :attr:`!__lazy_modules__`.
+
+Setting ``-X lazy_imports=none`` (or the :envvar:`PYTHON_LAZY_IMPORTS`
+environment variable to ``none``) overrides :attr:`!__lazy_modules__` and
+forces all imports to be eager.
+
+.. versionadded:: 3.15
+
 .. _future:
 
 Future statements
diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst
index dbdd5de01700a3..9ccd63bd8795f9 100644
--- a/Doc/whatsnew/3.15.rst
+++ b/Doc/whatsnew/3.15.rst
@@ -184,6 +184,18 @@ function, class body, or ``try``/``except``/``finally`` 
block raises a
 (``lazy from module import *`` and ``lazy from __future__ import ...`` both
 raise :exc:`SyntaxError`).
 
+For code that cannot use the ``lazy`` keyword directly (for example, when
+supporting Python versions older than 3.15 while still using lazy
+imports on 3.15+), a module can define
+:attr:`~module.__lazy_modules__` as a container of fully qualified module
+name strings.  Regular ``import`` statements for those modules are then treated
+as lazy, with the same semantics as the ``lazy`` keyword::
+
+   __lazy_modules__ = ["json", "pathlib"]
+
+   import json     # lazy
+   import os       # still eager
+
 .. seealso:: :pep:`810` for the full specification and rationale.
 
 (Contributed by Pablo Galindo Salgado and Dino Viehland in :gh:`142349`.)
diff --git a/Misc/NEWS.d/3.15.0a8.rst b/Misc/NEWS.d/3.15.0a8.rst
index ed37988f6ab548..ff7930aeb292d6 100644
--- a/Misc/NEWS.d/3.15.0a8.rst
+++ b/Misc/NEWS.d/3.15.0a8.rst
@@ -185,8 +185,8 @@ dealing with contradictions in ``make_bottom``.
 .. nonce: 6wDI6S
 .. section: Core and Builtins
 
-Ensure ``-X lazy_imports=none``` and ``PYTHON_LAZY_IMPORTS=none``` override
-``__lazy_modules__``. Patch by Hugo van Kemenade.
+Ensure ``-X lazy_imports=none`` and ``PYTHON_LAZY_IMPORTS=none`` override
+:attr:`~module.__lazy_modules__`. Patch by Hugo van Kemenade.
 
 ..
 

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to