Skip to content

Commit 7590438

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 fd252e8 commit 7590438

16 files changed

Lines changed: 68 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

@@ -1772,8 +1770,6 @@ always available. Unless explicitly noted otherwise, all variables are read-only
17721770
* ``"normal"``: Only imports explicitly marked with the ``lazy`` keyword
17731771
are lazy
17741772
* ``"all"``: All top-level imports become potentially lazy
1775-
* ``"none"``: All lazy imports are suppressed (even explicitly marked
1776-
ones)
17771773

17781774
This function is intended for advanced users who need to control lazy
17791775
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: 32 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -171,10 +171,18 @@ def test_from_import_with_imported_module_getattr(self):
171171
class GlobalLazyImportModeTests(LazyImportTestCase):
172172
"""Tests for sys.set_lazy_imports() global mode control."""
173173

174-
def test_global_off(self):
175-
"""Mode 'none' should disable lazy imports entirely."""
176-
import test.test_lazy_import.data.global_off
177-
self.assertIn("test.test_lazy_import.data.basic2", sys.modules)
174+
def tearDown(self):
175+
for key in list(sys.modules.keys()):
176+
if key.startswith('test.test_lazy_import.data'):
177+
del sys.modules[key]
178+
179+
sys.set_lazy_imports_filter(None)
180+
sys.set_lazy_imports("normal")
181+
182+
def test_global_off_rejected(self):
183+
"""Mode 'none' is not supported."""
184+
with self.assertRaises(ValueError):
185+
sys.set_lazy_imports("none")
178186

179187
def test_global_on(self):
180188
"""Mode 'all' should make regular imports lazy."""
@@ -612,9 +620,6 @@ def test_get_lazy_imports_returns_string(self):
612620
sys.set_lazy_imports("all")
613621
self.assertEqual(sys.get_lazy_imports(), "all")
614622

615-
sys.set_lazy_imports("none")
616-
self.assertEqual(sys.get_lazy_imports(), "none")
617-
618623
def test_get_lazy_imports_filter_default(self):
619624
"""get_lazy_imports_filter should return None by default."""
620625
sys.set_lazy_imports_filter(None)
@@ -1111,68 +1116,16 @@ def test_cli_lazy_imports_all_makes_regular_imports_lazy(self):
11111116
self.assertEqual(result.returncode, 0, f"stderr: {result.stderr}")
11121117
self.assertIn("LAZY", result.stdout)
11131118

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

11771130
def test_cli_lazy_imports_normal_respects_lazy_keyword_only(self):
11781131
"""-X lazy_imports=normal should respect lazy keyword only."""
@@ -1221,101 +1174,51 @@ def test_env_var_lazy_imports_all_enables_global_lazy(self):
12211174
self.assertEqual(result.returncode, 0, f"stderr: {result.stderr}")
12221175
self.assertIn("LAZY", result.stdout)
12231176

1224-
def test_env_var_lazy_imports_none_disables_all_lazy(self):
1225-
"""PYTHON_LAZY_IMPORTS=none should disable all lazy imports."""
1226-
code = textwrap.dedent("""
1227-
import sys
1228-
lazy import json
1229-
if 'json' in sys.modules:
1230-
print("EAGER")
1231-
else:
1232-
print("LAZY")
1233-
""")
1177+
def test_env_var_lazy_imports_none_is_rejected(self):
1178+
"""PYTHON_LAZY_IMPORTS=none should be rejected."""
12341179
import os
12351180
env = os.environ.copy()
12361181
env["PYTHON_LAZY_IMPORTS"] = "none"
12371182
result = subprocess.run(
1238-
[sys.executable, "-c", code],
1183+
[sys.executable, "-c", "pass"],
12391184
capture_output=True,
12401185
text=True,
12411186
env=env
12421187
)
1243-
self.assertEqual(result.returncode, 0, f"stderr: {result.stderr}")
1244-
self.assertIn("EAGER", result.stdout)
1245-
1246-
def test_cli_lazy_imports_none_disables_dunder_lazy_modules(self):
1247-
"""-X lazy_imports=none should override __lazy_modules__."""
1248-
code = textwrap.dedent("""
1249-
import sys
1250-
__lazy_modules__ = ["json"]
1251-
import json
1252-
if 'json' in sys.modules:
1253-
print("EAGER")
1254-
else:
1255-
print("LAZY")
1256-
""")
1257-
result = subprocess.run(
1258-
[sys.executable, "-X", "lazy_imports=none", "-c", code],
1259-
capture_output=True,
1260-
text=True,
1261-
)
1262-
self.assertEqual(result.returncode, 0, f"stderr: {result.stderr}")
1263-
self.assertIn("EAGER", result.stdout)
1264-
1265-
def test_env_var_lazy_imports_none_disables_dunder_lazy_modules(self):
1266-
"""PYTHON_LAZY_IMPORTS=none should override __lazy_modules__."""
1267-
code = textwrap.dedent("""
1268-
import sys
1269-
__lazy_modules__ = ["json"]
1270-
import json
1271-
if 'json' in sys.modules:
1272-
print("EAGER")
1273-
else:
1274-
print("LAZY")
1275-
""")
1276-
import os
1277-
1278-
env = os.environ.copy()
1279-
env["PYTHON_LAZY_IMPORTS"] = "none"
1280-
result = subprocess.run(
1281-
[sys.executable, "-c", code],
1282-
capture_output=True,
1283-
text=True,
1284-
env=env,
1285-
)
1286-
self.assertEqual(result.returncode, 0, f"stderr: {result.stderr}")
1287-
self.assertIn("EAGER", result.stdout)
1188+
self.assertNotEqual(result.returncode, 0)
1189+
self.assertIn("PYTHON_LAZY_IMPORTS: invalid value", result.stderr)
1190+
self.assertIn("expected 'all' or 'normal'", result.stderr)
12881191

12891192
def test_cli_overrides_env_var(self):
12901193
"""Command-line option should take precedence over environment variable."""
12911194
# PEP 810: -X lazy_imports takes precedence over PYTHON_LAZY_IMPORTS
12921195
code = textwrap.dedent("""
12931196
import sys
1294-
lazy import json
1197+
import json
12951198
if 'json' in sys.modules:
12961199
print("EAGER")
12971200
else:
12981201
print("LAZY")
12991202
""")
13001203
import os
13011204
env = os.environ.copy()
1302-
env["PYTHON_LAZY_IMPORTS"] = "all" # env says all
1205+
env["PYTHON_LAZY_IMPORTS"] = "all" # env says all imports are lazy
13031206
result = subprocess.run(
1304-
[sys.executable, "-X", "lazy_imports=none", "-c", code], # CLI says none
1207+
[sys.executable, "-X", "lazy_imports=normal", "-c", code],
13051208
capture_output=True,
13061209
text=True,
13071210
env=env
13081211
)
13091212
self.assertEqual(result.returncode, 0, f"stderr: {result.stderr}")
1310-
# CLI should win - imports should be eager
1213+
# CLI should win, so a regular import should stay eager.
13111214
self.assertIn("EAGER", result.stdout)
13121215

13131216
def test_sys_set_lazy_imports_overrides_cli(self):
13141217
"""sys.set_lazy_imports() should take precedence over CLI option."""
13151218
code = textwrap.dedent("""
13161219
import sys
1317-
sys.set_lazy_imports("none") # Override CLI
1318-
lazy import json
1220+
sys.set_lazy_imports("normal") # Override CLI
1221+
import json
13191222
if 'json' in sys.modules:
13201223
print("EAGER")
13211224
else:
@@ -2037,9 +1940,10 @@ def test_normal_import_dis(self):
20371940
class LazyCApiTests(LazyImportTestCase):
20381941
def test_set_matches_sys(self):
20391942
self.assertEqual(_testcapi.PyImport_GetLazyImportsMode(), sys.get_lazy_imports())
2040-
for mode in ("normal", "all", "none"):
1943+
for mode in ("normal", "all"):
20411944
_testcapi.PyImport_SetLazyImportsMode(mode)
20421945
self.assertEqual(_testcapi.PyImport_GetLazyImportsMode(), sys.get_lazy_imports())
1946+
self.assertRaises(ValueError, _testcapi.PyImport_SetLazyImportsMode, "none")
20431947

20441948
def test_filter_matches_sys(self):
20451949
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)