gh-138860: Lazy import rlcompleter in pdb to avoid deadlock in subprocess (#139185)
This commit is contained in:
15
Lib/pdb.py
15
Lib/pdb.py
@@ -100,7 +100,6 @@ import _colorize
|
|||||||
import _pyrepl.utils
|
import _pyrepl.utils
|
||||||
|
|
||||||
from contextlib import ExitStack, closing, contextmanager
|
from contextlib import ExitStack, closing, contextmanager
|
||||||
from rlcompleter import Completer
|
|
||||||
from types import CodeType
|
from types import CodeType
|
||||||
from warnings import deprecated
|
from warnings import deprecated
|
||||||
|
|
||||||
@@ -364,6 +363,15 @@ class Pdb(bdb.Bdb, cmd.Cmd):
|
|||||||
readline.set_completer_delims(' \t\n`@#%^&*()=+[{]}\\|;:\'",<>?')
|
readline.set_completer_delims(' \t\n`@#%^&*()=+[{]}\\|;:\'",<>?')
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
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.allow_kbdint = False
|
||||||
self.nosigint = nosigint
|
self.nosigint = nosigint
|
||||||
# Consider these characters as part of the command so when the users type
|
# 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', {})
|
conv_vars = self.curframe.f_globals.get('__pdb_convenience_variables', {})
|
||||||
return [f"${name}" for name in conv_vars if name.startswith(text[1:])]
|
return [f"${name}" for name in conv_vars if name.startswith(text[1:])]
|
||||||
|
|
||||||
# Use rlcompleter to do the completion
|
|
||||||
state = 0
|
state = 0
|
||||||
matches = []
|
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:
|
while (match := completer.complete(text, state)) is not None:
|
||||||
matches.append(match)
|
matches.append(match)
|
||||||
state += 1
|
state += 1
|
||||||
@@ -1204,8 +1211,8 @@ class Pdb(bdb.Bdb, cmd.Cmd):
|
|||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
completer = self.RlCompleter(ns)
|
||||||
old_completer = readline.get_completer()
|
old_completer = readline.get_completer()
|
||||||
completer = Completer(ns)
|
|
||||||
readline.set_completer(completer.complete)
|
readline.set_completer(completer.complete)
|
||||||
yield
|
yield
|
||||||
finally:
|
finally:
|
||||||
|
|||||||
@@ -4688,6 +4688,28 @@ class PdbTestInline(unittest.TestCase):
|
|||||||
stdout, _ = self._run_script(script, commands)
|
stdout, _ = self._run_script(script, commands)
|
||||||
self.assertIn("42", stdout)
|
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
|
@support.force_colorized_test_class
|
||||||
class PdbTestColorize(unittest.TestCase):
|
class PdbTestColorize(unittest.TestCase):
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
Lazy import :mod:`rlcompleter` in :mod:`pdb` to avoid deadlock in subprocess.
|
||||||
Reference in New Issue
Block a user