Skip to content

Commit 2e86583

Browse files
committed
[3.15] gh-149321: Remove lazy_imports=none startup mode (GH-149389)
(cherry picked from commit 1f3c267) Co-authored-by: Pablo Galindo Salgado <Pablogsal@gmail.com>
1 parent de401ef commit 2e86583

17 files changed

Lines changed: 148207 additions & 217 deletions

File tree

Doc/c-api/import.rst

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -393,11 +393,6 @@ Importing Modules
393393
394394
Make all imports lazy by default.
395395
396-
.. c:enumerator:: PyImport_LAZY_NONE
397-
398-
Disable lazy imports entirely. Even explicit ``lazy`` statements become
399-
eager imports.
400-
401396
.. versionadded:: 3.15
402397
403398
.. c:function:: PyObject* PyImport_CreateModuleFromInitfunc(PyObject *spec, PyObject* (*initfunc)(void))

Doc/library/sys.rst

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -919,8 +919,6 @@ always available. Unless explicitly noted otherwise, all variables are read-only
919919
* ``"normal"``: Only imports explicitly marked with the ``lazy`` keyword
920920
are lazy
921921
* ``"all"``: All top-level imports are potentially lazy
922-
* ``"none"``: All lazy imports are suppressed (even explicitly marked
923-
ones)
924922

925923
See also :func:`set_lazy_imports` and :pep:`810`.
926924

@@ -1757,8 +1755,6 @@ always available. Unless explicitly noted otherwise, all variables are read-only
17571755
* ``"normal"``: Only imports explicitly marked with the ``lazy`` keyword
17581756
are lazy
17591757
* ``"all"``: All top-level imports become potentially lazy
1760-
* ``"none"``: All lazy imports are suppressed (even explicitly marked
1761-
ones)
17621758

17631759
This function is intended for advanced users who need to control lazy
17641760
imports across their entire application. Library developers should

Doc/reference/simple_stmts.rst

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -965,10 +965,6 @@ Imports inside functions, class bodies, or
965965
:keyword:`try`/:keyword:`except`/:keyword:`finally` blocks are always eager,
966966
regardless of :attr:`!__lazy_modules__`.
967967

968-
Setting ``-X lazy_imports=none`` (or the :envvar:`PYTHON_LAZY_IMPORTS`
969-
environment variable to ``none``) overrides :attr:`!__lazy_modules__` and
970-
forces all imports to be eager.
971-
972968
.. versionadded:: 3.15
973969

974970
.. _future:

Doc/using/cmdline.rst

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -705,10 +705,9 @@ Miscellaneous options
705705

706706
.. versionadded:: 3.14
707707

708-
* :samp:`-X lazy_imports={all,none,normal}` controls lazy import behavior.
709-
``all`` makes all imports lazy by default, ``none`` disables lazy imports
710-
entirely (even explicit ``lazy`` statements become eager), and ``normal``
711-
(the default) respects the ``lazy`` keyword in source code.
708+
* :samp:`-X lazy_imports={all,normal}` controls lazy import behavior.
709+
``all`` makes all imports lazy by default, and ``normal`` (the default)
710+
respects the ``lazy`` keyword in source code.
712711
See also :envvar:`PYTHON_LAZY_IMPORTS`.
713712

714713
.. versionadded:: 3.15
@@ -1416,10 +1415,9 @@ conflict.
14161415

14171416
.. envvar:: PYTHON_LAZY_IMPORTS
14181417

1419-
Controls lazy import behavior. Accepts three values: ``all`` makes all
1420-
imports lazy by default, ``none`` disables lazy imports entirely (even
1421-
explicit ``lazy`` statements become eager), and ``normal`` (the default)
1422-
respects the ``lazy`` keyword in source code.
1418+
Controls lazy import behavior. Accepts two values: ``all`` makes all
1419+
imports lazy by default, and ``normal`` (the default) respects the
1420+
``lazy`` keyword in source code.
14231421

14241422
See also the :option:`-X lazy_imports <-X>` command-line option.
14251423

Doc/whatsnew/3.15.rst

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -157,11 +157,10 @@ making it straightforward to diagnose and debug the failure.
157157
For cases where you want to enable lazy loading globally without modifying
158158
source code, Python provides the :option:`-X lazy_imports <-X>` command-line
159159
option and the :envvar:`PYTHON_LAZY_IMPORTS` environment variable. Both
160-
accept three values: ``all`` makes all imports lazy by default, ``none``
161-
disables lazy imports entirely (even explicit ``lazy`` statements become
162-
eager), and ``normal`` (the default) respects the ``lazy`` keyword in source
163-
code. The :func:`sys.set_lazy_imports` and :func:`sys.get_lazy_imports`
164-
functions allow changing and querying this mode at runtime.
160+
accept two values: ``all`` makes all imports lazy by default, and ``normal``
161+
(the default) respects the ``lazy`` keyword in source code. The
162+
:func:`sys.set_lazy_imports` and :func:`sys.get_lazy_imports` functions allow
163+
changing and querying this mode at runtime.
165164

166165
For more selective control, :func:`sys.set_lazy_imports_filter` accepts a
167166
callable that determines whether a specific module should be loaded lazily.

Include/import.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,7 @@ PyAPI_FUNC(int) PyImport_AppendInittab(
9090

9191
typedef enum {
9292
PyImport_LAZY_NORMAL,
93-
PyImport_LAZY_ALL,
94-
PyImport_LAZY_NONE
93+
PyImport_LAZY_ALL
9594
} PyImport_LazyImportsMode;
9695

9796
#ifndef Py_LIMITED_API

Lib/test/test_lazy_import/__init__.py

Lines changed: 24 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,10 @@ def tearDown(self):
120120
sys.set_lazy_imports_filter(None)
121121
sys.set_lazy_imports("normal")
122122

123-
def test_global_off(self):
124-
"""Mode 'none' should disable lazy imports entirely."""
125-
import test.test_lazy_import.data.global_off
126-
self.assertIn("test.test_lazy_import.data.basic2", sys.modules)
123+
def test_global_off_rejected(self):
124+
"""Mode 'none' is not supported."""
125+
with self.assertRaises(ValueError):
126+
sys.set_lazy_imports("none")
127127

128128
def test_global_on(self):
129129
"""Mode 'all' should make regular imports lazy."""
@@ -576,9 +576,6 @@ def test_get_lazy_imports_returns_string(self):
576576
sys.set_lazy_imports("all")
577577
self.assertEqual(sys.get_lazy_imports(), "all")
578578

579-
sys.set_lazy_imports("none")
580-
self.assertEqual(sys.get_lazy_imports(), "none")
581-
582579
def test_get_lazy_imports_filter_default(self):
583580
"""get_lazy_imports_filter should return None by default."""
584581
sys.set_lazy_imports_filter(None)
@@ -1107,68 +1104,16 @@ def test_cli_lazy_imports_all_makes_regular_imports_lazy(self):
11071104
self.assertEqual(result.returncode, 0, f"stderr: {result.stderr}")
11081105
self.assertIn("LAZY", result.stdout)
11091106

1110-
def test_cli_lazy_imports_none_forces_all_imports_eager(self):
1111-
"""-X lazy_imports=none should force all imports to be eager."""
1112-
code = textwrap.dedent("""
1113-
import sys
1114-
# Even explicit lazy imports should be eager in 'none' mode
1115-
lazy import json
1116-
if 'json' in sys.modules:
1117-
print("EAGER")
1118-
else:
1119-
print("LAZY")
1120-
""")
1107+
def test_cli_lazy_imports_none_is_rejected(self):
1108+
"""-X lazy_imports=none should be rejected."""
11211109
result = subprocess.run(
1122-
[sys.executable, "-X", "lazy_imports=none", "-c", code],
1110+
[sys.executable, "-X", "lazy_imports=none", "-c", "pass"],
11231111
capture_output=True,
11241112
text=True
11251113
)
1126-
self.assertEqual(result.returncode, 0, f"stderr: {result.stderr}")
1127-
self.assertIn("EAGER", result.stdout)
1128-
1129-
@support.requires_resource("cpu")
1130-
def test_cli_lazy_imports_modes_import_stdlib_modules(self):
1131-
"""-X lazy_imports modes should import available stdlib modules."""
1132-
# Do not smoke-test modules with intentional import-time effects.
1133-
import_side_effect_modules = {"antigravity", "this"}
1134-
importable = []
1135-
1136-
for module in sorted(sys.stdlib_module_names):
1137-
if module in import_side_effect_modules:
1138-
continue
1139-
1140-
with self.subTest(module=module):
1141-
code = f"import {module}; print({module})"
1142-
baseline = subprocess.run(
1143-
[sys.executable, "-I", "-c", code],
1144-
capture_output=True,
1145-
text=True,
1146-
timeout=60,
1147-
)
1148-
if baseline.returncode:
1149-
# sys.stdlib_module_names includes modules for other
1150-
# platforms and optional extension modules not built here.
1151-
continue
1152-
importable.append(module)
1153-
1154-
for mode in ("normal", "none"):
1155-
with self.subTest(module=module, mode=mode):
1156-
result = subprocess.run(
1157-
[
1158-
sys.executable,
1159-
"-I",
1160-
"-X",
1161-
f"lazy_imports={mode}",
1162-
"-c",
1163-
code,
1164-
],
1165-
capture_output=True,
1166-
text=True,
1167-
timeout=60,
1168-
)
1169-
self.assertEqual(result.returncode, 0, result.stderr)
1170-
1171-
self.assertGreater(len(importable), 100)
1114+
self.assertNotEqual(result.returncode, 0)
1115+
self.assertIn("-X lazy_imports: invalid value", result.stderr)
1116+
self.assertIn("expected 'all' or 'normal'", result.stderr)
11721117

11731118
def test_cli_lazy_imports_normal_respects_lazy_keyword_only(self):
11741119
"""-X lazy_imports=normal should respect lazy keyword only."""
@@ -1217,101 +1162,51 @@ def test_env_var_lazy_imports_all_enables_global_lazy(self):
12171162
self.assertEqual(result.returncode, 0, f"stderr: {result.stderr}")
12181163
self.assertIn("LAZY", result.stdout)
12191164

1220-
def test_env_var_lazy_imports_none_disables_all_lazy(self):
1221-
"""PYTHON_LAZY_IMPORTS=none should disable all lazy imports."""
1222-
code = textwrap.dedent("""
1223-
import sys
1224-
lazy import json
1225-
if 'json' in sys.modules:
1226-
print("EAGER")
1227-
else:
1228-
print("LAZY")
1229-
""")
1165+
def test_env_var_lazy_imports_none_is_rejected(self):
1166+
"""PYTHON_LAZY_IMPORTS=none should be rejected."""
12301167
import os
12311168
env = os.environ.copy()
12321169
env["PYTHON_LAZY_IMPORTS"] = "none"
12331170
result = subprocess.run(
1234-
[sys.executable, "-c", code],
1171+
[sys.executable, "-c", "pass"],
12351172
capture_output=True,
12361173
text=True,
12371174
env=env
12381175
)
1239-
self.assertEqual(result.returncode, 0, f"stderr: {result.stderr}")
1240-
self.assertIn("EAGER", result.stdout)
1241-
1242-
def test_cli_lazy_imports_none_disables_dunder_lazy_modules(self):
1243-
"""-X lazy_imports=none should override __lazy_modules__."""
1244-
code = textwrap.dedent("""
1245-
import sys
1246-
__lazy_modules__ = ["json"]
1247-
import json
1248-
if 'json' in sys.modules:
1249-
print("EAGER")
1250-
else:
1251-
print("LAZY")
1252-
""")
1253-
result = subprocess.run(
1254-
[sys.executable, "-X", "lazy_imports=none", "-c", code],
1255-
capture_output=True,
1256-
text=True,
1257-
)
1258-
self.assertEqual(result.returncode, 0, f"stderr: {result.stderr}")
1259-
self.assertIn("EAGER", result.stdout)
1260-
1261-
def test_env_var_lazy_imports_none_disables_dunder_lazy_modules(self):
1262-
"""PYTHON_LAZY_IMPORTS=none should override __lazy_modules__."""
1263-
code = textwrap.dedent("""
1264-
import sys
1265-
__lazy_modules__ = ["json"]
1266-
import json
1267-
if 'json' in sys.modules:
1268-
print("EAGER")
1269-
else:
1270-
print("LAZY")
1271-
""")
1272-
import os
1273-
1274-
env = os.environ.copy()
1275-
env["PYTHON_LAZY_IMPORTS"] = "none"
1276-
result = subprocess.run(
1277-
[sys.executable, "-c", code],
1278-
capture_output=True,
1279-
text=True,
1280-
env=env,
1281-
)
1282-
self.assertEqual(result.returncode, 0, f"stderr: {result.stderr}")
1283-
self.assertIn("EAGER", result.stdout)
1176+
self.assertNotEqual(result.returncode, 0)
1177+
self.assertIn("PYTHON_LAZY_IMPORTS: invalid value", result.stderr)
1178+
self.assertIn("expected 'all' or 'normal'", result.stderr)
12841179

12851180
def test_cli_overrides_env_var(self):
12861181
"""Command-line option should take precedence over environment variable."""
12871182
# PEP 810: -X lazy_imports takes precedence over PYTHON_LAZY_IMPORTS
12881183
code = textwrap.dedent("""
12891184
import sys
1290-
lazy import json
1185+
import json
12911186
if 'json' in sys.modules:
12921187
print("EAGER")
12931188
else:
12941189
print("LAZY")
12951190
""")
12961191
import os
12971192
env = os.environ.copy()
1298-
env["PYTHON_LAZY_IMPORTS"] = "all" # env says all
1193+
env["PYTHON_LAZY_IMPORTS"] = "all" # env says all imports are lazy
12991194
result = subprocess.run(
1300-
[sys.executable, "-X", "lazy_imports=none", "-c", code], # CLI says none
1195+
[sys.executable, "-X", "lazy_imports=normal", "-c", code],
13011196
capture_output=True,
13021197
text=True,
13031198
env=env
13041199
)
13051200
self.assertEqual(result.returncode, 0, f"stderr: {result.stderr}")
1306-
# CLI should win - imports should be eager
1201+
# CLI should win, so a regular import should stay eager.
13071202
self.assertIn("EAGER", result.stdout)
13081203

13091204
def test_sys_set_lazy_imports_overrides_cli(self):
13101205
"""sys.set_lazy_imports() should take precedence over CLI option."""
13111206
code = textwrap.dedent("""
13121207
import sys
1313-
sys.set_lazy_imports("none") # Override CLI
1314-
lazy import json
1208+
sys.set_lazy_imports("normal") # Override CLI
1209+
import json
13151210
if 'json' in sys.modules:
13161211
print("EAGER")
13171212
else:
@@ -2093,9 +1988,10 @@ def tearDown(self):
20931988

20941989
def test_set_matches_sys(self):
20951990
self.assertEqual(_testcapi.PyImport_GetLazyImportsMode(), sys.get_lazy_imports())
2096-
for mode in ("normal", "all", "none"):
1991+
for mode in ("normal", "all"):
20971992
_testcapi.PyImport_SetLazyImportsMode(mode)
20981993
self.assertEqual(_testcapi.PyImport_GetLazyImportsMode(), sys.get_lazy_imports())
1994+
self.assertRaises(ValueError, _testcapi.PyImport_SetLazyImportsMode, "none")
20991995

21001996
def test_filter_matches_sys(self):
21011997
self.assertEqual(_testcapi.PyImport_GetLazyImportsFilter(), sys.get_lazy_imports_filter())

Lib/test/test_lazy_import/data/global_off.py

Lines changed: 0 additions & 5 deletions
This file was deleted.

Misc/NEWS.d/3.15.0a8.rst

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -180,16 +180,6 @@ dealing with contradictions in ``make_bottom``.
180180

181181
..
182182
183-
.. date: 2026-03-24-13-06-52
184-
.. gh-issue: 146369
185-
.. nonce: 6wDI6S
186-
.. section: Core and Builtins
187-
188-
Ensure ``-X lazy_imports=none`` and ``PYTHON_LAZY_IMPORTS=none`` override
189-
:attr:`~module.__lazy_modules__`. Patch by Hugo van Kemenade.
190-
191-
..
192-
193183
.. date: 2026-03-22-19-30-00
194184
.. gh-issue: 146308
195185
.. nonce: AxnRVA
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Do not support ``none`` as a lazy imports mode.

0 commit comments

Comments
 (0)