gh-130472: Remove readline-only hacks from PyREPL completions by pablogsal · Pull Request #148161 · python/cpython · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 7 additions & 16 deletions Lib/_pyrepl/fancycompleter.py
8 changes: 4 additions & 4 deletions Lib/_pyrepl/readline.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
from rlcompleter import Completer as RLCompleter

from . import commands, historical_reader
from .completing_reader import CompletingReader
from .completing_reader import CompletingReader, stripcolor
from .console import Console as ConsoleType
from ._module_completer import ModuleCompleter, make_default_module_completer
from .fancycompleter import Completer as FancyCompleter
Expand Down Expand Up @@ -163,9 +163,9 @@ def get_completions(self, stem: str) -> tuple[list[str], CompletionAction | None
break
result.append(next)
state += 1
# emulate the behavior of the standard readline that sorts
# the completions before displaying them.
result.sort()
# Emulate readline's sorting using the visible text rather than
# the raw ANSI escape sequences used for colorized matches.
result.sort(key=stripcolor)
return result, None

def get_module_completions(self) -> tuple[list[str], CompletionAction | None] | None:
Expand Down
34 changes: 13 additions & 21 deletions Lib/test/test_pyrepl/test_fancycompleter.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class C(object):
self.assertEqual(compl.attr_matches('a.'), ['a.attr', 'a.mro'])
self.assertEqual(
compl.attr_matches('a._'),
['a._C__attr__attr', 'a._attr', ' '],
['a._C__attr__attr', 'a._attr'],
)
matches = compl.attr_matches('a.__')
self.assertNotIn('__class__', matches)
Expand All @@ -79,7 +79,7 @@ def test_complete_attribute_colored(self):
break
else:
self.assertFalse(True, matches)
self.assertIn(' ', matches)
self.assertNotIn(' ', matches)

def test_preserves_callable_postfix_for_single_attribute_match(self):
compl = Completer({'os': os}, use_colors=False)
Expand Down Expand Up @@ -159,22 +159,17 @@ def test_complete_global_colored(self):
self.assertEqual(compl.global_matches('foo'), ['fooba'])
matches = compl.global_matches('fooba')

# these are the fake escape sequences which are needed so that
# readline displays the matches in the proper order
N0 = f"\x1b[000;00m"
N1 = f"\x1b[000;01m"
int_color = theme.fancycompleter.int
self.assertEqual(set(matches), {
' ',
f'{N0}{int_color}foobar{ANSIColors.RESET}',
f'{N1}{int_color}foobazzz{ANSIColors.RESET}',
})
self.assertEqual(matches, [
f'{int_color}foobar{ANSIColors.RESET}',
f'{int_color}foobazzz{ANSIColors.RESET}',
])
self.assertEqual(compl.global_matches('foobaz'), ['foobazzz'])
self.assertEqual(compl.global_matches('nothing'), [])

def test_large_color_sort_prefix_is_stripped(self):
def test_colorized_match_is_stripped(self):
compl = Completer({'a': 42}, use_colors=True)
match = compl._color_for_obj(1000, 'spam', 1)
match = compl._color_for_obj('spam', 1)
self.assertEqual(stripcolor(match), 'spam')

def test_complete_with_indexer(self):
Expand All @@ -197,27 +192,24 @@ class A:
compl = Completer({'A': A}, use_colors=False)
#
# In this case, we want to display all attributes which start with
# 'a'. Moreover, we also include a space to prevent readline to
# automatically insert the common prefix (which will the the ANSI escape
# sequence if we use colors).
# 'a'.
matches = compl.attr_matches('A.a')
self.assertEqual(
sorted(matches),
[' ', 'A.aaa', 'A.abc_1', 'A.abc_2', 'A.abc_3'],
['A.aaa', 'A.abc_1', 'A.abc_2', 'A.abc_3'],
)
#
# If there is an actual common prefix, we return just it, so that readline
# will insert it into place
matches = compl.attr_matches('A.ab')
self.assertEqual(matches, ['A.abc_'])
#
# Finally, at the next tab, we display again all the completions available
# for this common prefix. Again, we insert a spurious space to prevent the
# automatic completion of ANSI sequences.
# Finally, at the next tab, we display again all the completions
# available for this common prefix.
matches = compl.attr_matches('A.abc_')
self.assertEqual(
sorted(matches),
[' ', 'A.abc_1', 'A.abc_2', 'A.abc_3'],
['A.abc_1', 'A.abc_2', 'A.abc_3'],
)

def test_complete_exception(self):
Expand Down
22 changes: 22 additions & 0 deletions Lib/test/test_pyrepl/test_pyrepl.py
Loading