Skip to content

Commit 8288f36

Browse files
gh-139289: Lazy import rlcompleter to fix the refleak (#139305)
1 parent f04bea4 commit 8288f36

File tree

3 files changed

+29
-11
lines changed

3 files changed

+29
-11
lines changed

Lib/pdb.py

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -364,14 +364,6 @@ def __init__(self, completekey='tab', stdin=None, stdout=None, skip=None,
364364
except ImportError:
365365
pass
366366

367-
# GH-138860
368-
# We need to lazy-import rlcompleter to avoid deadlock
369-
# We cannot import it during self.complete* methods because importing
370-
# rlcompleter for the first time will overwrite readline's completer
371-
# So we import it here and save the Completer class
372-
from rlcompleter import Completer
373-
self.RlCompleter = Completer
374-
375367
self.allow_kbdint = False
376368
self.nosigint = nosigint
377369
# Consider these characters as part of the command so when the users type
@@ -1100,6 +1092,31 @@ def set_convenience_variable(self, frame, name, value):
11001092
# Generic completion functions. Individual complete_foo methods can be
11011093
# assigned below to one of these functions.
11021094

1095+
@property
1096+
def rlcompleter(self):
1097+
"""Return the `Completer` class from `rlcompleter`, while avoiding the
1098+
side effects of changing the completer from `import rlcompleter`.
1099+
1100+
This is a compromise between GH-138860 and GH-139289. If GH-139289 is
1101+
fixed, then we don't need this and we can just `import rlcompleter` in
1102+
`Pdb.__init__`.
1103+
"""
1104+
if not hasattr(self, "_rlcompleter"):
1105+
try:
1106+
import readline
1107+
except ImportError:
1108+
# readline is not available, just get the Completer
1109+
from rlcompleter import Completer
1110+
self._rlcompleter = Completer
1111+
else:
1112+
# importing rlcompleter could have side effect of changing
1113+
# the current completer, we need to restore it
1114+
prev_completer = readline.get_completer()
1115+
from rlcompleter import Completer
1116+
self._rlcompleter = Completer
1117+
readline.set_completer(prev_completer)
1118+
return self._rlcompleter
1119+
11031120
def completenames(self, text, line, begidx, endidx):
11041121
# Overwrite completenames() of cmd so for the command completion,
11051122
# if no current command matches, check for expressions as well
@@ -1196,7 +1213,7 @@ def completedefault(self, text, line, begidx, endidx):
11961213

11971214
state = 0
11981215
matches = []
1199-
completer = self.RlCompleter(self.curframe.f_globals | self.curframe.f_locals)
1216+
completer = self.rlcompleter(self.curframe.f_globals | self.curframe.f_locals)
12001217
while (match := completer.complete(text, state)) is not None:
12011218
matches.append(match)
12021219
state += 1
@@ -1211,7 +1228,7 @@ def _enable_rlcompleter(self, ns):
12111228
return
12121229

12131230
try:
1214-
completer = self.RlCompleter(ns)
1231+
completer = self.rlcompleter(ns)
12151232
old_completer = readline.get_completer()
12161233
readline.set_completer(completer.complete)
12171234
yield

Lib/test/test_pyclbr.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ def test_others(self):
250250
'pdb',
251251
# pyclbr does not handle elegantly `typing` or properties
252252
ignore=('Union', '_ModuleTarget', '_ScriptTarget', '_ZipTarget', 'curframe_locals',
253-
'_InteractState'),
253+
'_InteractState', 'rlcompleter'),
254254
)
255255
cm('pydoc', ignore=('input', 'output',)) # properties
256256

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Do a real lazy-import on :mod:`rlcompleter` in :mod:`pdb` and restore the existing completer after importing :mod:`rlcompleter`.

0 commit comments

Comments
 (0)