single: __doc__ (module attribute)
single: __annotations__ (module attribute)
single: __annotate__ (module attribute)
+ single: __lazy_modules__ (module attribute)
pair: module; namespace
.. _import-mod-attrs:
.. 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
^^^^^^^^^^^^^^^^^^^
.. 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
(``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`.)