From: Tian Gao Date: Wed, 24 Sep 2025 03:46:05 +0000 (+0200) Subject: gh-138860: Lazy import rlcompleter in pdb to avoid deadlock in subprocess (#139185) X-Git-Tag: v3.15.0a1~217 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=c8624cd36746b17d8f991cde63705e9419e940de;p=thirdparty%2FPython%2Fcpython.git gh-138860: Lazy import rlcompleter in pdb to avoid deadlock in subprocess (#139185) --- diff --git a/Lib/pdb.py b/Lib/pdb.py index a783583a2b1c..fd48882e28fe 100644 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -100,7 +100,6 @@ import _colorize import _pyrepl.utils from contextlib import ExitStack, closing, contextmanager -from rlcompleter import Completer from types import CodeType from warnings import deprecated @@ -364,6 +363,15 @@ class Pdb(bdb.Bdb, cmd.Cmd): readline.set_completer_delims(' \t\n`@#%^&*()=+[{]}\\|;:\'",<>?') except ImportError: pass + + # GH-138860 + # We need to lazy-import rlcompleter to avoid deadlock + # We cannot import it during self.complete* methods because importing + # rlcompleter for the first time will overwrite readline's completer + # So we import it here and save the Completer class + from rlcompleter import Completer + self.RlCompleter = Completer + self.allow_kbdint = False self.nosigint = nosigint # Consider these characters as part of the command so when the users type @@ -1186,10 +1194,9 @@ class Pdb(bdb.Bdb, cmd.Cmd): conv_vars = self.curframe.f_globals.get('__pdb_convenience_variables', {}) return [f"${name}" for name in conv_vars if name.startswith(text[1:])] - # Use rlcompleter to do the completion state = 0 matches = [] - completer = Completer(self.curframe.f_globals | self.curframe.f_locals) + completer = self.RlCompleter(self.curframe.f_globals | self.curframe.f_locals) while (match := completer.complete(text, state)) is not None: matches.append(match) state += 1 @@ -1204,8 +1211,8 @@ class Pdb(bdb.Bdb, cmd.Cmd): return try: + completer = self.RlCompleter(ns) old_completer = readline.get_completer() - completer = Completer(ns) readline.set_completer(completer.complete) yield finally: diff --git a/Lib/test/test_pdb.py b/Lib/test/test_pdb.py index 6b74e21ad73d..9a7d85500355 100644 --- a/Lib/test/test_pdb.py +++ b/Lib/test/test_pdb.py @@ -4688,6 +4688,28 @@ class PdbTestInline(unittest.TestCase): stdout, _ = self._run_script(script, commands) self.assertIn("42", stdout) + def test_readline_not_imported(self): + """GH-138860 + Directly or indirectly importing readline might deadlock a subprocess + if it's launched with process_group=0 or preexec_fn=setpgrp + + It's also a pattern that readline is never imported with just import pdb. + + This test is to ensure that readline is not imported for import pdb. + It's possible that we have a good reason to do that in the future. + """ + + script = textwrap.dedent(""" + import sys + import pdb + if "readline" in sys.modules: + print("readline imported") + """) + commands = "" + stdout, stderr = self._run_script(script, commands) + self.assertNotIn("readline imported", stdout) + self.assertEqual(stderr, "") + @support.force_colorized_test_class class PdbTestColorize(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2025-09-20-17-50-31.gh-issue-138860.Y9JXap.rst b/Misc/NEWS.d/next/Library/2025-09-20-17-50-31.gh-issue-138860.Y9JXap.rst new file mode 100644 index 000000000000..0903eb71ae43 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-09-20-17-50-31.gh-issue-138860.Y9JXap.rst @@ -0,0 +1 @@ +Lazy import :mod:`rlcompleter` in :mod:`pdb` to avoid deadlock in subprocess.