Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
543527b
gh-148588: Document __lazy_modules__ compatibility mode for lazy imports
johnslavik Apr 15, 2026
8891a25
gh-148588: Add cross-ref link to __lazy_modules__ in NEWS, drop docs …
johnslavik Apr 15, 2026
f4a8366
gh-148588: Fix __lazy_modules__ use-case description
johnslavik Apr 15, 2026
a65ba21
gh-148588: Remove em dashes from __lazy_modules__ docs
johnslavik Apr 15, 2026
c99c1a4
gh-148588: Drop 'generated programmatically' aside from __lazy_module…
johnslavik Apr 15, 2026
907366e
gh-148588: Use :term:`iterable` not 'sequence' for __lazy_modules__
johnslavik Apr 15, 2026
2005434
gh-148588: Use 'fully qualified module name' consistently
johnslavik Apr 15, 2026
e86de6b
gh-148588: Add relative import example to __lazy_modules__ docs
johnslavik Apr 15, 2026
b6e14eb
gh-148588: Drop 'optional' from __lazy_modules__ iterable description
johnslavik Apr 15, 2026
6507959
gh-148588: Clarify __lazy_modules__ with relative import example
johnslavik Apr 15, 2026
e9c0d3b
gh-148588: Use 'container' not 'iterable' for __lazy_modules__
johnslavik Apr 15, 2026
f24d587
gh-148588: Clarify __lazy_modules__ must be a container (__contains__…
johnslavik Apr 15, 2026
5f73065
gh-148588: Drop redundant __contains__ clarification from simple_stmt…
johnslavik Apr 15, 2026
7900e32
Apply suggestions from code review
johnslavik Apr 15, 2026
ba4d7e0
Fix ref to lazy modules as container
johnslavik Apr 24, 2026
923c8e7
Bring back two critical lines
johnslavik Apr 25, 2026
d4ea69b
Rephrase more clearly
johnslavik Apr 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions Doc/reference/datamodel.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
^^^^^^^^^^^^^^^^^^^

Expand Down
50 changes: 50 additions & 0 deletions Doc/reference/simple_stmts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 12 additions & 0 deletions Doc/whatsnew/3.15.rst
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,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`.)
Expand Down
4 changes: 2 additions & 2 deletions Misc/NEWS.d/3.15.0a8.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.

..
Expand Down
Loading