]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.14] gh-138860: Lazy import rlcompleter in pdb to avoid deadlock in subprocess...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Fri, 10 Oct 2025 14:23:52 +0000 (16:23 +0200)
committerGitHub <noreply@github.com>
Fri, 10 Oct 2025 14:23:52 +0000 (16:23 +0200)
* gh-138860: Lazy import rlcompleter in pdb to avoid deadlock in subprocess (GH-139185)
(cherry picked from commit c8624cd36746b17d8f991cde63705e9419e940de)

* gh-139289: Lazy import rlcompleter to fix the refleak (GH-139305)
(cherry picked from commit 8288f3693f50058ad9b9fe04e01f5dad902d8bad)

Co-authored-by: Tian Gao <gaogaotiantian@hotmail.com>
Lib/pdb.py
Lib/test/test_pdb.py
Lib/test/test_pyclbr.py
Misc/NEWS.d/next/Library/2025-09-20-17-50-31.gh-issue-138860.Y9JXap.rst [new file with mode: 0644]
Misc/NEWS.d/next/Library/2025-09-24-14-17-34.gh-issue-139289.Vmk25k.rst [new file with mode: 0644]

index 2d7da2f82bea813bafe31b0969d5f06530aec766..14ed25ed147011c41957b23223c9946cff7a3167 100644 (file)
@@ -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,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
             readline.set_completer_delims(' \t\n`@#%^&*()=+[{]}\\|;:\'",<>?')
         except ImportError:
             pass
+
         self.allow_kbdint = False
         self.nosigint = nosigint
         # Consider these characters as part of the command so when the users type
@@ -1092,6 +1092,31 @@ class Pdb(bdb.Bdb, cmd.Cmd):
     # Generic completion functions.  Individual complete_foo methods can be
     # assigned below to one of these functions.
 
+    @property
+    def rlcompleter(self):
+        """Return the `Completer` class from `rlcompleter`, while avoiding the
+        side effects of changing the completer from `import rlcompleter`.
+
+        This is a compromise between GH-138860 and GH-139289. If GH-139289 is
+        fixed, then we don't need this and we can just `import rlcompleter` in
+        `Pdb.__init__`.
+        """
+        if not hasattr(self, "_rlcompleter"):
+            try:
+                import readline
+            except ImportError:
+                # readline is not available, just get the Completer
+                from rlcompleter import Completer
+                self._rlcompleter = Completer
+            else:
+                # importing rlcompleter could have side effect of changing
+                # the current completer, we need to restore it
+                prev_completer = readline.get_completer()
+                from rlcompleter import Completer
+                self._rlcompleter = Completer
+                readline.set_completer(prev_completer)
+        return self._rlcompleter
+
     def completenames(self, text, line, begidx, endidx):
         # Overwrite completenames() of cmd so for the command completion,
         # if no current command matches, check for expressions as well
@@ -1186,10 +1211,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 +1228,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:
index 6b74e21ad73d1a2453a1eb83cdf4a608f8a17edc..9a7d855003551ae4bec127038afbf0163f59abb5 100644 (file)
@@ -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):
index 3e7b2cd0dc99126401137fa70aa36127a4f60427..3062f4eb9be5571d5e5aaafefbc5d50a7f993def 100644 (file)
@@ -254,7 +254,7 @@ class PyclbrTest(TestCase):
                 'pdb',
                 # pyclbr does not handle elegantly `typing` or properties
                 ignore=('Union', '_ModuleTarget', '_ScriptTarget', '_ZipTarget', 'curframe_locals',
-                        '_InteractState'),
+                        '_InteractState', 'rlcompleter'),
             )
         cm('pydoc', ignore=('input', 'output',))  # properties
 
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 (file)
index 0000000..0903eb7
--- /dev/null
@@ -0,0 +1 @@
+Lazy import :mod:`rlcompleter` in :mod:`pdb` to avoid deadlock in subprocess.
diff --git a/Misc/NEWS.d/next/Library/2025-09-24-14-17-34.gh-issue-139289.Vmk25k.rst b/Misc/NEWS.d/next/Library/2025-09-24-14-17-34.gh-issue-139289.Vmk25k.rst
new file mode 100644 (file)
index 0000000..0416261
--- /dev/null
@@ -0,0 +1 @@
+Do a real lazy-import on :mod:`rlcompleter` in :mod:`pdb` and restore the existing completer after importing :mod:`rlcompleter`.