https://github.com/python/cpython/commit/00348beb6767451fcbab218e12acdec684a979f8
commit: 00348beb6767451fcbab218e12acdec684a979f8
branch: main
author: Hugo van Kemenade <[email protected]>
committer: hugovk <[email protected]>
date: 2026-10-21T10:35:49+03:00
summary:

gh-154675: Use lazy imports in `pdb`, `bdb`, `code` (#156567)

files:
A Misc/NEWS.d/next/Library/2026-08-29-13-41-56.gh-issue-154675.cYvE-Z.rst
M Lib/bdb.py
M Lib/code.py
M Lib/pdb.py
M Lib/test/test_bdb.py
M Lib/test/test_code_module.py
M Lib/test/test_pdb.py

diff --git a/Lib/bdb.py b/Lib/bdb.py
index 7acd359855ffc6..2ff52aebfc9822 100644
--- a/Lib/bdb.py
+++ b/Lib/bdb.py
@@ -1,12 +1,12 @@
 """Debugger basics"""
 
-import fnmatch
-import sys
-import threading
 import os
-import weakref
+import sys
 from contextlib import contextmanager
 from inspect import CO_GENERATOR, CO_COROUTINE, CO_ASYNC_GENERATOR
+lazy import fnmatch
+lazy import threading
+lazy import weakref
 
 __all__ = ["BdbQuit", "Bdb", "Breakpoint"]
 
diff --git a/Lib/code.py b/Lib/code.py
index f658049197e1b7..d429947ecc86b0 100644
--- a/Lib/code.py
+++ b/Lib/code.py
@@ -7,8 +7,8 @@
 
 import builtins
 import sys
-import traceback
 from codeop import CommandCompiler, compile_command
+lazy import traceback
 
 __all__ = ["InteractiveInterpreter", "InteractiveConsole", "interact",
            "compile_command"]
diff --git a/Lib/pdb.py b/Lib/pdb.py
index e03f4fcf62cf3f..db48c00d2f477d 100644
--- a/Lib/pdb.py
+++ b/Lib/pdb.py
@@ -67,46 +67,44 @@
 
 import os
 import io
-import re
 import sys
 import cmd
-import bdb
 import dis
+import bdb
 import code
-import glob
-import json
 import stat
 import token
 import types
 import atexit
 import codeop
-import pprint
-import signal
-import socket
 import typing
 import inspect
-import weakref
 import builtins
-import tempfile
-import textwrap
-import tokenize
 import itertools
-import traceback
 import linecache
-import selectors
-import threading
-import _colorize
 
 from contextlib import ExitStack, closing, contextmanager
 from types import CodeType
 from warnings import deprecated
 
+lazy import _colorize
+lazy import argparse
 lazy import asyncio
-
-try:
-    import _pyrepl.utils
-except ModuleNotFoundError:
-    _pyrepl = None
+lazy import glob
+lazy import json
+lazy import pprint
+lazy import re
+lazy import runpy
+lazy import selectors
+lazy import shlex
+lazy import signal
+lazy import socket
+lazy import tempfile
+lazy import textwrap
+lazy import threading
+lazy import tokenize
+lazy import traceback
+lazy import weakref
 
 
 class Restart(Exception):
@@ -247,7 +245,6 @@ class _ModuleTarget(_ExecutableTarget):
     def __init__(self, target):
         self._target = target
 
-        import runpy
         try:
             _, self._spec, self._code = runpy._get_module_details(self._target)
         except ImportError as e:
@@ -282,8 +279,6 @@ def namespace(self):
 
 class _ZipTarget(_ExecutableTarget):
     def __init__(self, target):
-        import runpy
-
         self._target = os.path.realpath(target)
         sys.path.insert(0, self._target)
         try:
@@ -466,6 +461,7 @@ def complete(self, text, state):
             return None
 
     def gen_colors(self, buffer):
+        import _pyrepl.utils
         from _pyrepl.utils import ColorSpan, Span
 
         if not buffer.strip():
@@ -1271,7 +1267,11 @@ def handle_command_def(self, line):
         return False
 
     def _colorize_code(self, code):
-        if self.colorize and _pyrepl:
+        if self.colorize:
+            try:
+                import _pyrepl.utils
+            except ModuleNotFoundError:
+                return code
             colors = list(_pyrepl.utils.gen_colors(code))
             chars, _ = _pyrepl.utils.disp_str(code, colors=colors, 
force_color=True)
             code = "".join(chars)
@@ -2089,7 +2089,6 @@ def do_run(self, arg):
                        'e.g. "python -m pdb myscript.py"')
             return
         if arg:
-            import shlex
             argv0 = sys.argv[0:1]
             try:
                 sys.argv = shlex.split(arg)
@@ -3787,9 +3786,7 @@ def parse_args():
     # "python -m pdb -m foo -p 1" should pass "-p 1" to "foo".
     # "python -m pdb foo.py -m bar" should pass "-m bar" to "foo.py".
     # "python -m pdb -m foo -m bar" should pass "-m bar" to "foo".
-    # This require some customized parsing logic to find the actual debug 
target.
-
-    import argparse
+    # This requires some customized parsing logic to find the actual debug 
target.
 
     parser = argparse.ArgumentParser(
         usage="%(prog)s [-h] [-c command] (-m module | -p pid | pyfile) [args 
...]",
diff --git a/Lib/test/test_bdb.py b/Lib/test/test_bdb.py
index 8805d098ff22dc..749d5eaef82eef 100644
--- a/Lib/test/test_bdb.py
+++ b/Lib/test/test_bdb.py
@@ -57,6 +57,7 @@
 import linecache
 from contextlib import contextmanager
 from itertools import islice, repeat
+from test.support import cpython_only
 from test.support import import_helper
 from test.support import os_helper
 from test.support import patch_list
@@ -1243,6 +1244,10 @@ def test_next_to_botframe(self):
 
 
 class TestRegressions(unittest.TestCase):
+    @cpython_only
+    def test_lazy_import(self):
+        import_helper.ensure_lazy_imports("bdb", {"fnmatch", "threading", 
"weakref"})
+
     def test_format_stack_entry_no_lineno(self):
         # See gh-101517
         self.assertIn('Warning: lineno is None',
diff --git a/Lib/test/test_code_module.py b/Lib/test/test_code_module.py
index c0c3454a97fbd8..944e0e5655e085 100644
--- a/Lib/test/test_code_module.py
+++ b/Lib/test/test_code_module.py
@@ -5,6 +5,7 @@
 from textwrap import dedent
 from contextlib import ExitStack
 from unittest import mock
+from test.support import cpython_only
 from test.support import force_not_colorized_test_class
 from test.support import import_helper
 
@@ -38,6 +39,10 @@ def setUp(self):
         self.console = code.InteractiveConsole()
         self.mock_sys()
 
+    @cpython_only
+    def test_lazy_import(self):
+        import_helper.ensure_lazy_imports("code", {"traceback"})
+
     def test_ps1(self):
         self.infunc.side_effect = [
             "import code",
diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py
index b3c11db79a7b93..51a3e2212a13cf 100644
--- a/Lib/test/test_pdb.py
+++ b/Lib/test/test_pdb.py
@@ -22,7 +22,7 @@
 from io import StringIO
 from test import support
 from test.support import has_socket_support, os_helper
-from test.support.import_helper import import_module
+from test.support.import_helper import ensure_lazy_imports, import_module
 from test.support.pty_helper import run_pty, FakeInput
 from test.support.script_helper import kill_python
 from unittest.mock import patch
@@ -3558,6 +3558,32 @@ class PdbTestCase(unittest.TestCase):
     def tearDown(self):
         os_helper.unlink(os_helper.TESTFN)
 
+    @support.cpython_only
+    def test_lazy_import(self):
+        ensure_lazy_imports(
+            "pdb",
+            {
+                "_colorize",
+                "argparse",
+                "asyncio",
+                "glob",
+                "json",
+                "pprint",
+                "re",
+                "runpy",
+                "selectors",
+                "shlex",
+                "signal",
+                "socket",
+                "tempfile",
+                "textwrap",
+                "threading",
+                "tokenize",
+                "traceback",
+                "weakref",
+            },
+        )
+
     @unittest.skipIf(sys.flags.safe_path,
                      'PYTHONSAFEPATH changes default sys.path')
     def _run_pdb(self, pdb_args, commands,
diff --git 
a/Misc/NEWS.d/next/Library/2026-08-29-13-41-56.gh-issue-154675.cYvE-Z.rst 
b/Misc/NEWS.d/next/Library/2026-08-29-13-41-56.gh-issue-154675.cYvE-Z.rst
new file mode 100644
index 00000000000000..02d4996702b458
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-08-29-13-41-56.gh-issue-154675.cYvE-Z.rst
@@ -0,0 +1,2 @@
+Improve import time of :mod:`pdb`, :mod:`bdb` and :mod:`code` by lazily
+importing several dependencies.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to