@@ -335,19 +335,29 @@ An import remains lazy only if the filter function returns ``True``.
335335
336336If no lazy import filter is set, all *potentially lazy * imports are lazy.
337337
338+ Lazy objects
339+ ------------
340+
341+ Lazy modules, as well as names lazy imported from modules, are represented
342+ by :class: `!types.LazyImportType ` instances, which are resolved to the real
343+ object (reified) before they can be used. This reification is usually done
344+ automatically (see below), but can also be done by calling the lazy object's
345+ ``get `` method.
346+
338347Lazy import mechanism
339348---------------------
340349
341350When an import is lazy, ``__lazy_import__ `` is called instead of
342351``__import__ ``. ``__lazy_import__ `` has the same function signature as
343352``__import__ ``. It adds the module name to ``sys.lazy_modules ``, a set of
344353fully-qualified module names which have been lazily imported at some point
345- (primarily for diagnostics and introspection), and returns a "lazy module
346- object."
354+ (primarily for diagnostics and introspection), and returns a
355+ :class: ` !types.LazyImportType` ` object for the module.
347356
348357The implementation of ``from ... import `` (the ``IMPORT_FROM `` bytecode
349358implementation) checks if the module it's fetching from is a lazy module
350- object, and if so, returns a lazy object for each name instead.
359+ object, and if so, returns a :class: `!types.LazyImportType ` for each name
360+ instead.
351361
352362The end result of this process is that lazy imports (regardless of how they
353363are enabled) result in lazy objects being assigned to global variables.
@@ -356,34 +366,36 @@ Lazy module objects do not appear in ``sys.modules``, they're just listed in
356366the ``sys.lazy_modules `` set. Under normal operation lazy objects should only
357367end up stored in global variables, and the common ways to access those
358368variables (regular variable access, module attributes) will resolve lazy
359- imports (" reify" ) and replace them when they're accessed.
369+ imports (reify) and replace them when they're accessed.
360370
361371It is still possible to expose lazy objects through other means, like
362372debuggers. This is not considered a problem.
363373
364374Reification
365375-----------
366376
367- When a lazy object is first used, it needs to be reified. This means resolving
368- the import at that point in the program and replacing the lazy object with the
369- concrete one. Reification imports the module in the same way as it would have
370- been if it had been imported eagerly. Notably, reification still calls
371- ``__import__ `` to resolve the import, which uses the state of the import system
372- (e.g. ``sys.path ``, ``sys.meta_path ``, ``sys.path_hooks `` and ``__import__ ``)
373- at **reification ** time, **not ** the state when the ``lazy import `` statement
374- was evaluated.
375-
376- When the module is first reified, it's removed from ``sys.lazy_modules `` (even
377- if there are still other unreified lazy references to it). When a package is
377+ When a lazy object is used, it needs to be reified. This means resolving the
378+ import at that point in the program and replacing the lazy object with the
379+ concrete one. Reification imports the module at that point in the program.
380+ Notably, reification still calls ``__import__ `` to resolve the import, which
381+ uses the state of the import system (e.g. ``sys.path ``, ``sys.meta_path ``,
382+ ``sys.path_hooks `` and ``__import__ ``) at **reification ** time, **not ** the
383+ state when the ``lazy import `` statement was evaluated.
384+
385+ When the module is reified, it's removed from ``sys.lazy_modules `` (even if
386+ there are still other unreified lazy references to it). When a package is
378387reified and submodules in the package were also previously lazily imported,
379388those submodules are *not * automatically reified but they *are * added to the
380- reified package's globals (unless the package already assigned something else
381- to the name of the submodule).
389+ reified package's globals (unless the package already assigned something
390+ else to the name of the submodule).
382391
383- If reification fails (e.g., due to an ``ImportError ``), the exception is
384- enhanced with chaining to show both where the lazy import was defined and
385- where it was first accessed (even though it propagates from the code that
386- triggered reification). This provides clear debugging information:
392+ If reification fails (e.g., due to an ``ImportError ``), the lazy object is
393+ *not * reified or replaced. Subsequent uses of the lazy object will re-try
394+ the reification. Exceptions that happen during reification are raised as
395+ normal, but the exception is enhanced with chaining to show both where the
396+ lazy import was defined and where it was accessed (even though it propagates
397+ from the code that triggered reification). This provides clear debugging
398+ information:
387399
388400.. code-block :: python
389401
@@ -428,8 +440,9 @@ referenced the module. It **only** resolves the lazy object being accessed.
428440
429441Accessing a lazy object (from a global variable or a module attribute) reifies
430442the object. Accessing a module's ``__dict__ `` reifies **all ** lazy objects in
431- that module. Operations that indirectly access ``__dict__ `` (such as
432- :func: `dir `) also trigger this behavior.
443+ that module. Calling ``dir() `` at the global scope will not reify the globals
444+ and calling ``dir(mod) `` will be special cased in ``mod.__dir__ `` avoid
445+ reification as well.
433446
434447Example using ``__dict__ `` from external code:
435448
@@ -469,7 +482,7 @@ Example using ``globals()``:
469482 g = globals ()
470483
471484 print (' json' in sys.modules) # False - still lazy
472- print (type (g[' json' ])) # <class 'lazy_import '>
485+ print (type (g[' json' ])) # <class 'LazyImport '>
473486
474487 # Explicitly reify using the get() method
475488 resolved = g[' json' ].get()
@@ -484,6 +497,9 @@ Reference Implementation
484497A reference implementation is available at:
485498https://github.com/LazyImportsCabal/cpython/tree/lazy
486499
500+ A demo is available (not necessarily synced with the latest PEP) for
501+ evaluation purposes at: https://lazy-import-demo.pages.dev/
502+
487503Bytecode and adaptive specialization
488504-------------------------------------
489505
@@ -704,7 +720,7 @@ These changes are limited to bindings explicitly made lazy:
704720
705721* **Error timing. ** Exceptions that would have occurred during an eager import
706722 (for example ``ImportError `` or ``AttributeError `` for a missing member) now
707- occur at the first *use * of the lazy name.
723+ occur at the *use * of the lazy name.
708724
709725 .. code-block :: python
710726
@@ -714,7 +730,7 @@ These changes are limited to bindings explicitly made lazy:
714730 # With lazy import - error deferred
715731 lazy import broken_module
716732 print (" Import succeeded" )
717- broken_module.foo() # ImportError raised here on first use
733+ broken_module.foo() # ImportError raised here on use
718734
719735 * **Side-effect timing. ** Import-time side effects in lazily imported modules
720736 occur at first use of the binding, not at module import time.
@@ -727,16 +743,17 @@ These changes are limited to bindings explicitly made lazy:
727743 when it is first used.
728744* **Proxy visibility. ** Before first use, the bound name refers to a lazy
729745 proxy. Indirect introspection that touches the value may observe a proxy
730- lazy object representation. After first use, the name is rebound to the real
731- object and becomes indistinguishable from an eager import.
746+ lazy object representation. After first use (provied the module was
747+ imported succesfully), the name is rebound to the real object and becomes
748+ indistinguishable from an eager import.
732749
733750Thread-safety and reification
734751-----------------------------
735752
736- First use of a lazy binding follows the existing import-lock discipline.
737- Exactly one thread performs the import and **atomically rebinds ** the
738- importing module's global to the resolved object. Concurrent readers
739- thereafter observe the real object.
753+ Reification follows the existing import-lock discipline. Exactly one thread
754+ performs the import and **atomically rebinds ** the importing module's global
755+ to the resolved object. Concurrent readers thereafter observe the real
756+ object.
740757
741758Lazy imports are thread-safe and have no special considerations for
742759free-threading. A module that would normally be imported in the main thread
@@ -758,11 +775,11 @@ code that doesn't.
758775Runtime performance
759776~~~~~~~~~~~~~~~~~~~
760777
761- After reification (first use ), lazy imports have ** zero overhead **. The
762- adaptive interpreter specializes the bytecode (typically after 2-3 accesses),
763- eliminating any checks. For example, `` LOAD_GLOBAL `` becomes
764- ``LOAD_GLOBAL_MODULE ``, which directly accesses the module identically to
765- normal imports.
778+ After reification (provided the import was succesful ), lazy imports have
779+ ** zero overhead **. The adaptive interpreter specializes the bytecode
780+ (typically after 2-3 accesses), eliminating any checks. For example,
781+ ``LOAD_GLOBAL `` becomes `` LOAD_GLOBAL_MODULE ``, which directly accesses the
782+ module identically to normal imports.
766783
767784The `pyperformance suite `_ confirms the implementation is performance-neutral.
768785
@@ -1033,6 +1050,9 @@ it was first used:
10331050 1/0
10341051 ZeroDivisionError: division by zero
10351052
1053+ Exceptions during reification prevent the replacement of the lazy object,
1054+ and subsequent uses of the lazy object will retry the whole reification.
1055+
10361056How do lazy imports affect modules with import-time side effects?
10371057-----------------------------------------------------------------
10381058
@@ -1118,8 +1138,8 @@ What's the performance overhead of lazy imports?
11181138
11191139The overhead is minimal:
11201140
1121- - Zero overhead after first use thanks to the adaptive interpreter optimizing
1122- the slow path away.
1141+ - Zero overhead after first use (provided the import doesn't fail) thanks to
1142+ the adaptive interpreter optimizing the slow path away.
11231143- Small one-time cost to create the proxy object.
11241144- Reification (first use) has the same cost as a regular import.
11251145- No ongoing performance penalty.
@@ -1159,7 +1179,7 @@ error. If lazy imports are globally enabled, star imports will still be eager.
11591179How do lazy imports interact with import hooks and custom loaders?
11601180------------------------------------------------------------------
11611181
1162- Import hooks and loaders work normally. When a lazy object is first used,
1182+ Import hooks and loaders work normally. When a lazy object is used,
11631183the standard import protocol runs, including any custom hooks or loaders that
11641184were in place at reification time.
11651185
@@ -1191,7 +1211,7 @@ reifies all lazy imports in that module first. This design ensures:
11911211 lazy import json
11921212
11931213 g = globals ()
1194- print (type (g[' json' ])) # <class 'lazy_import '> - your problem
1214+ print (type (g[' json' ])) # <class 'LazyImport '> - your problem
11951215
11961216 # From external code:
11971217 import sys
@@ -1313,9 +1333,8 @@ How do lazy imports interact with ``dir()``, ``getattr()``, and module introspec
13131333-------------------------------------------------------------------------------------
13141334
13151335Accessing lazy imports through normal attribute access or ``getattr() ``
1316- will trigger reification. Calling ``dir() `` on a module will reify all lazy
1317- imports in that module to ensure the directory listing is complete. This is
1318- similar to accessing ``mod.__dict__ ``.
1336+ will trigger reification of the accessed attribute. Calling ``dir() `` on a
1337+ module will be special cased in ``mod.__dir__ `` to avoid reification.
13191338
13201339.. code-block :: python
13211340
@@ -1327,7 +1346,6 @@ similar to accessing ``mod.__dict__``.
13271346 # Any of these trigger reification:
13281347 dumps_func = json.dumps
13291348 dumps_func = getattr (json, ' dumps' )
1330- dir (json)
13311349 # Now json is in sys.modules
13321350
13331351 Do lazy imports work with circular imports?
@@ -1390,11 +1408,11 @@ The best practice is still to avoid circular imports in your code design.
13901408Will lazy imports affect the performance of my hot paths?
13911409---------------------------------------------------------
13921410
1393- After first use, lazy imports have **zero overhead ** thanks to the adaptive
1394- interpreter. The interpreter specializes the bytecode (e.g., `` LOAD_GLOBAL ``
1395- becomes ``LOAD_GLOBAL_MODULE ``) which eliminates the lazy check on subsequent
1396- accesses. This means once a lazy import is reified, accessing it is just as
1397- fast as a normal import.
1411+ After first use (provided the import succeed) , lazy imports have **zero
1412+ overhead ** thanks to the adaptive interpreter. The interpreter specializes
1413+ the bytecode (e.g., `` LOAD_GLOBAL `` becomes ``LOAD_GLOBAL_MODULE ``) which
1414+ eliminates the lazy check on subsequent accesses. This means once a lazy
1415+ import is reified, accessing it is just as fast as a normal import.
13981416
13991417.. code-block :: python
14001418
0 commit comments