Skip to content

Commit 9bcbe0a

Browse files
authored
Merge pull request #38 from Yhg1s/reflow-reformat
2 parents f2adb04 + 1572241 commit 9bcbe0a

26 files changed

+338
-228
lines changed

Doc/library/sys.rst

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -916,9 +916,11 @@ always available. Unless explicitly noted otherwise, all variables are read-only
916916

917917
Returns the current lazy imports mode as a string.
918918

919-
* ``"normal"``: Only imports explicitly marked with the ``lazy`` keyword are lazy
919+
* ``"normal"``: Only imports explicitly marked with the ``lazy`` keyword
920+
are lazy
920921
* ``"all"``: All top-level imports are potentially lazy
921-
* ``"none"``: All lazy imports are suppressed (even explicitly marked ones)
922+
* ``"none"``: All lazy imports are suppressed (even explicitly marked
923+
ones)
922924

923925
See also :func:`set_lazy_imports` and :pep:`810`.
924926

@@ -927,23 +929,25 @@ always available. Unless explicitly noted otherwise, all variables are read-only
927929

928930
.. function:: get_lazy_imports_filter()
929931

930-
Returns the current lazy imports filter function, or ``None`` if no filter
931-
is set.
932+
Returns the current lazy imports filter function, or ``None`` if no
933+
filter is set.
932934

933-
The filter function is called for every potentially lazy import to determine
934-
whether it should actually be lazy. See :func:`set_lazy_imports_filter` for
935-
details on the filter function signature.
935+
The filter function is called for every potentially lazy import to
936+
determine whether it should actually be lazy. See
937+
:func:`set_lazy_imports_filter` for details on the filter function
938+
signature.
936939

937940
.. versionadded:: next
938941

939942

940943
.. function:: get_lazy_modules()
941944

942-
Returns a set of fully-qualified module names that have been lazily imported.
943-
This is primarily useful for diagnostics and introspection.
945+
Returns a set of fully-qualified module names that have been
946+
lazilyimported. This is primarily useful for diagnostics and
947+
introspection.
944948

945-
Note that modules are removed from this set when they are reified (actually
946-
loaded on first use).
949+
Note that modules are removed from this set when they are reified
950+
(actually loaded on first use).
947951

948952
.. versionadded:: next
949953

@@ -1759,16 +1763,19 @@ always available. Unless explicitly noted otherwise, all variables are read-only
17591763

17601764
.. function:: set_lazy_imports(mode)
17611765

1762-
Sets the global lazy imports mode. The *mode* parameter must be one of the
1763-
following strings:
1766+
Sets the global lazy imports mode. The *mode* parameter must be one of
1767+
the following strings:
17641768

1765-
* ``"normal"``: Only imports explicitly marked with the ``lazy`` keyword are lazy
1769+
* ``"normal"``: Only imports explicitly marked with the ``lazy`` keyword
1770+
are lazy
17661771
* ``"all"``: All top-level imports become potentially lazy
1767-
* ``"none"``: All lazy imports are suppressed (even explicitly marked ones)
1772+
* ``"none"``: All lazy imports are suppressed (even explicitly marked
1773+
ones)
17681774

1769-
This function is intended for advanced users who need to control lazy imports
1770-
across their entire application. Library developers should generally not use
1771-
this function as it affects the runtime execution of applications.
1775+
This function is intended for advanced users who need to control lazy
1776+
imports across their entire application. Library developers should
1777+
generally not use this function as it affects the runtime execution of
1778+
applications.
17721779

17731780
In addition to the mode, lazy imports can be controlled via the filter
17741781
provided by :func:`set_lazy_imports_filter`.
@@ -1783,8 +1790,9 @@ always available. Unless explicitly noted otherwise, all variables are read-only
17831790
Sets the lazy imports filter callback. The *filter* parameter must be a
17841791
callable or ``None`` to clear the filter.
17851792

1786-
The filter function is called for every potentially lazy import to determine
1787-
whether it should actually be lazy. It must have the following signature::
1793+
The filter function is called for every potentially lazy import to
1794+
determine whether it should actually be lazy. It must have the following
1795+
signature::
17881796

17891797
def filter(importing_module: str, imported_module: str,
17901798
fromlist: tuple[str, ...] | None) -> bool

Doc/whatsnew/3.15.rst

Lines changed: 57 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -87,29 +87,31 @@ New features
8787
:pep:`810`: Explicit lazy imports
8888
---------------------------------
8989

90-
Large Python applications often suffer from slow startup times. A significant
91-
contributor to this problem is the import system: when a module is imported,
92-
Python must locate the file, read it from disk, compile it to bytecode, and
93-
execute all top-level code. For applications with deep dependency trees, this
94-
process can take seconds, even when most of the imported code is never actually
95-
used during a particular run.
90+
Large Python applications often suffer from slow startup times. A
91+
significant contributor to this problem is the import system: when a module
92+
is imported, Python must locate the file, read it from disk, compile it to
93+
bytecode, and execute all top-level code. For applications with deep
94+
dependency trees, this process can take seconds, even when most of the
95+
imported code is never actually used during a particular run.
9696

9797
Developers have worked around this by moving imports inside functions, using
9898
:mod:`importlib` to load modules on demand, or restructuring code to avoid
9999
unnecessary dependencies. These approaches work but make code harder to read
100100
and maintain, scatter import statements throughout the codebase, and require
101101
discipline to apply consistently.
102102

103-
Python now provides a cleaner solution through explicit :keyword:`lazy` imports
104-
using the new ``lazy`` soft keyword. When you mark an import as lazy, Python
105-
defers the actual module loading until the imported name is first used. This
106-
gives you the organizational benefits of declaring all imports at the top of
107-
the file while only paying the loading cost for modules you actually use.
103+
Python now provides a cleaner solution through explicit :keyword:`lazy`
104+
imports using the new ``lazy`` soft keyword. When you mark an import as
105+
lazy, Python defers the actual module loading until the imported name is
106+
first used. This gives you the organizational benefits of declaring all
107+
imports at the top of the file while only paying the loading cost for
108+
modules you actually use.
108109

109-
The ``lazy`` keyword works with both ``import`` and ``from ... import`` statements.
110-
When you write ``lazy import heavy_module``, Python does not immediately load the
111-
module. Instead, it creates a lightweight proxy object. The actual module loading
112-
happens transparently when you first access the name:
110+
The ``lazy`` keyword works with both ``import`` and ``from ... import``
111+
statements. When you write ``lazy import heavy_module``, Python does not
112+
immediately load the module. Instead, it creates a lightweight proxy object.
113+
The actual module loading happens transparently when you first access the
114+
name:
113115

114116
.. code-block:: python
115117
@@ -121,54 +123,59 @@ happens transparently when you first access the name:
121123
data = json.loads('{"key": "value"}') # json gets loads here
122124
now = datetime() # datetime loads here
123125
124-
This mechanism is particularly useful for applications that import many modules
125-
at the top level but may only use a subset of them in any given run. The deferred
126-
loading reduces startup latency without requiring code restructuring or conditional
127-
imports scattered throughout the codebase.
128-
129-
In the case where loading a lazily imported module fails (for example, if the
130-
module does not exist), Python raises the exception at the point of first use
131-
rather than at import time. The associated traceback includes both the location
132-
where the name was accessed and the original import statement, making it
133-
straightforward to diagnose & debug the failure.
134-
135-
For cases where you want to enable lazy loading globally without modifying source
136-
code, Python provides the :option:`-X lazy_imports <-X>` command-line option and
137-
the :envvar:`PYTHON_LAZY_IMPORTS` environment variable. Both accept three values:
138-
``all`` makes all imports lazy by default, ``none`` disables lazy imports entirely
139-
(even explicit ``lazy`` statements become eager), and ``normal`` (the default)
140-
respects the ``lazy`` keyword in source code. The :func:`sys.set_lazy_imports` and
141-
:func:`sys.get_lazy_imports` functions allow changing and querying this mode at
142-
runtime.
143-
144-
For more selective control, :func:`sys.set_lazy_imports_filter` accepts a callable
145-
that determines whether a specific module should be loaded lazily. The filter
146-
receives three arguments: the importing module's name (or ``None``), the imported
147-
module's name, and the fromlist (or ``None`` for regular imports). It should
148-
return ``True`` to allow the import to be lazy, or ``False`` to force eager loading.
149-
This allows patterns like making only your own application's modules lazy while
150-
keeping third-party dependencies eager:
126+
This mechanism is particularly useful for applications that import many
127+
modules at the top level but may only use a subset of them in any given run.
128+
The deferred loading reduces startup latency without requiring code
129+
restructuring or conditional imports scattered throughout the codebase.
130+
131+
In the case where loading a lazily imported module fails (for example, if
132+
the module does not exist), Python raises the exception at the point of
133+
first use rather than at import time. The associated traceback includes both
134+
the location where the name was accessed and the original import statement,
135+
making it straightforward to diagnose & debug the failure.
136+
137+
For cases where you want to enable lazy loading globally without modifying
138+
source code, Python provides the :option:`-X lazy_imports <-X>` command-line
139+
option and the :envvar:`PYTHON_LAZY_IMPORTS` environment variable. Both
140+
accept three values: ``all`` makes all imports lazy by default, ``none``
141+
disables lazy imports entirely (even explicit ``lazy`` statements become
142+
eager), and ``normal`` (the default) respects the ``lazy`` keyword in source
143+
code. The :func:`sys.set_lazy_imports` and :func:`sys.get_lazy_imports`
144+
functions allow changing and querying this mode at runtime.
145+
146+
For more selective control, :func:`sys.set_lazy_imports_filter` accepts a
147+
callable that determines whether a specific module should be loaded lazily.
148+
The filter receives three arguments: the importing module's name (or
149+
``None``), the imported module's name, and the fromlist (or ``None`` for
150+
regular imports). It should return ``True`` to allow the import to be lazy,
151+
or ``False`` to force eager loading. This allows patterns like making only
152+
your own application's modules lazy while keeping third-party dependencies
153+
eager:
151154

152155
.. code-block:: python
153156
154157
import sys
155158
156-
sys.set_lazy_imports_filter(lambda importing, imported, fromlist: imported.startswith("myapp."))
159+
def myapp_filter(importing, imported, fromlist):
160+
return imported.startswith("myapp.")
161+
sys.set_lazy_imports_filter(myapp_filter)
157162
sys.set_lazy_imports("all")
158163
159164
import myapp.slow_module # lazy (matches filter)
160165
import json # eager (does not match filter)
161166
162167
For debugging and introspection, :func:`sys.get_lazy_modules` returns a set
163-
containing the names of all modules that have been lazily imported but not yet
164-
loaded. The proxy type itself is available as :data:`types.LazyImportType` for
165-
code that needs to detect lazy imports programmatically.
168+
containing the names of all modules that have been lazily imported but not
169+
yet loaded. The proxy type itself is available as
170+
:data:`types.LazyImportType` for code that needs to detect lazy imports
171+
programmatically.
166172

167173
There are some restrictions on where the ``lazy`` keyword can be used. Lazy
168-
imports are only permitted at module scope; using ``lazy`` inside a function,
169-
class body, or ``try``/``except``/``finally`` block raises a :exc:`SyntaxError`.
170-
Neither star imports nor future imports can be lazy (``lazy from module import *``
171-
and ``lazy from __future__ import ...`` both raise :exc:`SyntaxError`).
174+
imports are only permitted at module scope; using ``lazy`` inside a
175+
function, class body, or ``try``/``except``/``finally`` block raises a
176+
:exc:`SyntaxError`. Neither star imports nor future imports can be lazy
177+
(``lazy from module import *`` and ``lazy from __future__ import ...`` both
178+
raise :exc:`SyntaxError`).
172179

173180
.. seealso:: :pep:`810` for the full specification and rationale.
174181

Include/internal/pycore_ceval.h

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -311,14 +311,19 @@ PyAPI_FUNC(void) _PyEval_FormatExcCheckArg(PyThreadState *tstate, PyObject *exc,
311311
PyAPI_FUNC(void) _PyEval_FormatExcUnbound(PyThreadState *tstate, PyCodeObject *co, int oparg);
312312
PyAPI_FUNC(void) _PyEval_FormatKwargsError(PyThreadState *tstate, PyObject *func, PyObject *kwargs);
313313
PyAPI_FUNC(PyObject *) _PyEval_ImportFrom(PyThreadState *, PyObject *, PyObject *);
314-
PyAPI_FUNC(PyObject *) _PyEval_LazyImportName(PyThreadState *tstate, PyObject *builtins, PyObject *globals,
315-
PyObject *locals, PyObject *name, PyObject *fromlist, PyObject *level, int lazy);
316-
PyAPI_FUNC(PyObject *) _PyEval_LazyImportFrom(PyThreadState *tstate, PyObject *v, PyObject *name);
317-
PyAPI_FUNC(PyObject *) _PyEval_ImportName(PyThreadState *tstate, PyObject *builtins, PyObject *globals, PyObject *locals,
318-
PyObject *name, PyObject *fromlist, PyObject *level);
319-
PyObject *
320-
_PyEval_ImportNameWithImport(PyThreadState *tstate, PyObject *import_func, PyObject *globals, PyObject *locals,
321-
PyObject *name, PyObject *fromlist, PyObject *level);
314+
315+
PyAPI_FUNC(PyObject *) _PyEval_LazyImportName(
316+
PyThreadState *tstate, PyObject *builtins, PyObject *globals,
317+
PyObject *locals, PyObject *name, PyObject *fromlist, PyObject *level,
318+
int lazy);
319+
PyAPI_FUNC(PyObject *) _PyEval_LazyImportFrom(
320+
PyThreadState *tstate, PyObject *v, PyObject *name);
321+
PyAPI_FUNC(PyObject *) _PyEval_ImportName(
322+
PyThreadState *tstate, PyObject *builtins, PyObject *globals,
323+
PyObject *locals, PyObject *name, PyObject *fromlist, PyObject *level);
324+
PyObject * _PyEval_ImportNameWithImport(
325+
PyThreadState *tstate, PyObject *import_func, PyObject *globals,
326+
PyObject *locals, PyObject *name, PyObject *fromlist, PyObject *level);
322327
PyAPI_FUNC(PyObject *)_PyEval_MatchClass(PyThreadState *tstate, PyObject *subject, PyObject *type, Py_ssize_t nargs, PyObject *kwargs);
323328
PyAPI_FUNC(PyObject *)_PyEval_MatchKeys(PyThreadState *tstate, PyObject *map, PyObject *keys);
324329
PyAPI_FUNC(void) _PyEval_MonitorRaise(PyThreadState *tstate, _PyInterpreterFrame *frame, _Py_CODEUNIT *instr);

Include/internal/pycore_dict.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ extern int _PyDict_Contains_KnownHash(PyObject *, PyObject *, Py_hash_t);
4040
extern PyObject* _PyDict_GetItemIdWithError(PyObject *dp,
4141
_Py_Identifier *key);
4242
extern int _PyDict_ContainsId(PyObject *, _Py_Identifier *);
43-
extern int _PyDict_SetItemId(PyObject *dp, _Py_Identifier *key, PyObject *item);
43+
extern int _PyDict_SetItemId(PyObject *dp, _Py_Identifier *key,
44+
PyObject *item);
4445
extern int _PyDict_DelItemId(PyObject *mp, _Py_Identifier *key);
4546
extern void _PyDict_ClearKeysVersion(PyObject *mp);
4647

Include/internal/pycore_import.h

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,16 @@ extern int _PyImport_FixupBuiltin(
3232
PyObject *modules
3333
);
3434

35-
extern PyObject *
36-
_PyImport_ResolveName(PyThreadState *tstate, PyObject *name, PyObject *globals, int level);
37-
extern PyObject *
38-
_PyImport_GetAbsName(PyThreadState *tstate, PyObject *name, PyObject *globals, int level);
35+
extern PyObject * _PyImport_ResolveName(
36+
PyThreadState *tstate, PyObject *name, PyObject *globals, int level);
37+
extern PyObject * _PyImport_GetAbsName(
38+
PyThreadState *tstate, PyObject *name, PyObject *globals, int level);
3939
// Symbol is exported for the JIT on Windows builds.
40-
PyAPI_FUNC(PyObject *)
41-
_PyImport_LoadLazyImportTstate(PyThreadState *tstate, PyObject *lazy_import);
42-
extern PyObject *
43-
_PyImport_LazyImportModuleLevelObject(PyThreadState *tstate, PyObject *name, PyObject *builtins, PyObject *globals,
44-
PyObject *locals, PyObject *fromlist,
45-
int level);
40+
PyAPI_FUNC(PyObject *) _PyImport_LoadLazyImportTstate(
41+
PyThreadState *tstate, PyObject *lazy_import);
42+
extern PyObject * _PyImport_LazyImportModuleLevelObject(
43+
PyThreadState *tstate, PyObject *name, PyObject *builtins,
44+
PyObject *globals, PyObject *locals, PyObject *fromlist, int level);
4645

4746

4847
#ifdef HAVE_DLOPEN
@@ -82,7 +81,8 @@ extern void _PyImport_ClearModules(PyInterpreterState *interp);
8281

8382
extern void _PyImport_ClearModulesByIndex(PyInterpreterState *interp);
8483

85-
extern PyObject * _PyImport_InitLazyModules(PyInterpreterState *interp);
84+
extern PyObject * _PyImport_InitLazyModules(
85+
PyInterpreterState *interp);
8686
extern void _PyImport_ClearLazyModules(PyInterpreterState *interp);
8787

8888
extern int _PyImport_InitDefaultImportFunc(PyInterpreterState *interp);

Include/internal/pycore_lazyimportobject.h

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
/* File added for Lazy Imports */
2-
3-
/* Lazy object interface */
1+
// Lazy object interface.
42

53
#ifndef Py_INTERNAL_LAZYIMPORTOBJECT_H
64
#define Py_INTERNAL_LAZYIMPORTOBJECT_H
@@ -21,16 +19,17 @@ typedef struct {
2119
PyObject *lz_builtins;
2220
PyObject *lz_from;
2321
PyObject *lz_attr;
24-
/* Frame information for the original import location */
25-
PyCodeObject *lz_code; /* code object where the lazy import was created */
26-
int lz_instr_offset; /* instruction offset where the lazy import was created */
22+
// Frame information for the original import location.
23+
PyCodeObject *lz_code; // Code object where the lazy import was created.
24+
int lz_instr_offset; // Instruction offset where the lazy import was created.
2725
} PyLazyImportObject;
2826

2927

3028
PyAPI_FUNC(PyObject *) _PyLazyImport_GetName(PyObject *lazy_import);
31-
PyAPI_FUNC(PyObject *) _PyLazyImport_New(PyObject *import_func, PyObject *from, PyObject *attr);
29+
PyAPI_FUNC(PyObject *) _PyLazyImport_New(
30+
PyObject *import_func, PyObject *from, PyObject *attr);
3231

3332
#ifdef __cplusplus
3433
}
3534
#endif
36-
#endif /* !Py_INTERNAL_LAZYIMPORTOBJECT_H */
35+
#endif // !Py_INTERNAL_LAZYIMPORTOBJECT_H

Include/internal/pycore_moduleobject.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ extern Py_ssize_t _PyModule_GetFilenameUTF8(
7878
PyObject* _Py_module_getattro_impl(PyModuleObject *m, PyObject *name, int suppress);
7979
PyObject* _Py_module_getattro(PyObject *m, PyObject *name);
8080

81-
PyAPI_FUNC(int) _PyModule_ReplaceLazyValue(PyObject *dict, PyObject *name, PyObject *value);
81+
PyAPI_FUNC(int) _PyModule_ReplaceLazyValue(
82+
PyObject *dict, PyObject *name, PyObject *value);
8283

8384
#ifdef __cplusplus
8485
}

Lib/dis.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1019,7 +1019,8 @@ def _find_imports(co):
10191019
(level_op[0] in hasconst or level_op[0] == LOAD_SMALL_INT)):
10201020
level = _get_const_value(level_op[0], level_op[1], consts)
10211021
fromlist = _get_const_value(from_op[0], from_op[1], consts)
1022-
# IMPORT_NAME encodes lazy/eager flags in bits 0-1, name index in bits 2+
1022+
# IMPORT_NAME encodes lazy/eager flags in bits 0-1,
1023+
# name index in bits 2+.
10231024
yield (names[oparg >> 2], level, fromlist)
10241025

10251026
def _find_store_names(co):

Lib/idlelib/idle_test/test_colorizer.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -545,9 +545,11 @@ def test_case_soft_keyword(self):
545545
def test_lazy_soft_keyword(self):
546546
# lazy followed by import
547547
self._assert_highlighting('lazy import foo',
548-
{'KEYWORD': [('1.0', '1.4'), ('1.5', '1.11')]})
548+
{'KEYWORD': [('1.0', '1.4'),
549+
('1.5', '1.11')]})
549550
self._assert_highlighting(' lazy import foo',
550-
{'KEYWORD': [('1.4', '1.8'), ('1.9', '1.15')]})
551+
{'KEYWORD': [('1.4', '1.8'),
552+
('1.9', '1.15')]})
551553

552554
# lazy followed by from
553555
self._assert_highlighting('lazy from foo import bar',

Lib/rlcompleter.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,8 @@ def attr_matches(self, text):
192192

193193
if (isinstance(thisobject, types.ModuleType)
194194
and
195-
isinstance(thisobject.__dict__.get(word), types.LazyImportType)
195+
isinstance(thisobject.__dict__.get(word),
196+
types.LazyImportType)
196197
):
197198
value = thisobject.__dict__.get(word)
198199
else:

0 commit comments

Comments
 (0)