gh-139289: Lazy import rlcompleter to fix the refleak (#139305)

This commit is contained in:
Tian Gao
2025-09-26 22:17:02 +08:00
committed by GitHub
parent f04bea44c3
commit 8288f3693f
3 changed files with 29 additions and 11 deletions

View File

@@ -364,14 +364,6 @@ class Pdb(bdb.Bdb, cmd.Cmd):
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
@@ -1100,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
@@ -1196,7 +1213,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
state = 0
matches = []
completer = self.RlCompleter(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
@@ -1211,7 +1228,7 @@ class Pdb(bdb.Bdb, cmd.Cmd):
return
try:
completer = self.RlCompleter(ns)
completer = self.rlcompleter(ns)
old_completer = readline.get_completer()
readline.set_completer(completer.complete)
yield

View File

@@ -250,7 +250,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

View File

@@ -0,0 +1 @@
Do a real lazy-import on :mod:`rlcompleter` in :mod:`pdb` and restore the existing completer after importing :mod:`rlcompleter`.