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]