Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
gh-999998: Speed up pdb startup with lazy imports
Defer the eight imports that are not already pulled in by pdb's
required-eager imports (cmd, bdb, code, typing and the guarded
_pyrepl.utils), and lift five unguarded function-level imports to
module-scope lazy imports.

Guard Pdb._get_asyncio_task() on 'asyncio' in sys.modules. It runs on every
interaction(), so without the guard the deferred asyncio import is paid back
immediately on the first breakpoint and the lazy import buys nothing for an
actual debugging session. If asyncio was never imported there cannot be a
running task, so returning None is equivalent.

The guarded imports (readline, rlcompleter, _pyrepl) stay function-level:
they sit in try/except ImportError blocks, and hoisting them would move the
failure outside the guard.

import pdb              -42.1%
breakpoint() -> cont    -41.1%
python -m pdb script    -33.4%
pdb post-mortem         -33.3%
  • Loading branch information
brittanyrey committed Sep 1, 2026
commit 3c199f4ab5f4225e690e8ecfdd5cc581e649caca
31 changes: 16 additions & 15 deletions Lib/pdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,28 +73,32 @@
import bdb
import dis
import code
import glob
import json
lazy import glob
lazy import json
import stat
lazy import runpy
lazy import shlex
lazy import pydoc
import token
import types
import atexit
import codeop
import pprint
import signal
import socket
lazy import pprint
lazy import signal
lazy import socket
import typing
import asyncio
lazy import asyncio
Comment thread
brittanyrey marked this conversation as resolved.
Outdated
import inspect
import weakref
lazy import argparse
import builtins
import tempfile
lazy import tempfile
import textwrap
import tokenize
import itertools
import traceback
import linecache
import selectors
lazy import selectors
import threading
import _colorize

Expand Down Expand Up @@ -246,7 +250,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:
Expand Down Expand Up @@ -281,8 +284,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:
Expand Down Expand Up @@ -902,6 +903,10 @@ def _hold_exceptions(self, exceptions):
self._chained_exception_index = 0

def _get_asyncio_task(self):
# If asyncio has never been imported there cannot be a running task,
# so skip the import rather than pay for it on every interaction.
if 'asyncio' not in sys.modules:
return None
try:
task = asyncio.current_task()
except RuntimeError:
Expand Down Expand Up @@ -2084,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)
Expand Down Expand Up @@ -3739,7 +3743,6 @@ def test():

# print help
def help():
import pydoc
pydoc.pager(__doc__)

_usage = """\
Expand Down Expand Up @@ -3780,8 +3783,6 @@ def parse_args():
# "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

parser = argparse.ArgumentParser(
usage="%(prog)s [-h] [-c command] (-m module | -p pid | pyfile) [args ...]",
description=_usage,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Speed up :mod:`pdb` startup by about 40% using :keyword:`lazy import`, and by
skipping the :mod:`asyncio` import when no event loop has been used.
Loading