-
-
Notifications
You must be signed in to change notification settings - Fork 32.2k
gh-130472: Integrate fancycompleter with the new repl, to get colored tab completions #130473
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
…'t work because they need to be ported from pytest to unittest
…n exception when evaluated, just color it as None
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exciting! Just some nits.
BOLD_BLUE = "\x1b[1;34m" | ||
BOLD_GREEN = "\x1b[1;32m" | ||
BOLD_MAGENTA = "\x1b[1;35m" | ||
BOLD_RED = "\x1b[1;31m" | ||
BOLD_TEAL = "\x1b[1;36m" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ANSI colour with code 36 is more commonly called cyan: https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
BOLD_BLUE = "\x1b[1;34m" | |
BOLD_GREEN = "\x1b[1;32m" | |
BOLD_MAGENTA = "\x1b[1;35m" | |
BOLD_RED = "\x1b[1;31m" | |
BOLD_TEAL = "\x1b[1;36m" | |
BOLD_BLUE = "\x1b[1;34m" | |
BOLD_CYAN = "\x1b[1;36m" | |
BOLD_GREEN = "\x1b[1;32m" | |
BOLD_MAGENTA = "\x1b[1;35m" | |
BOLD_RED = "\x1b[1;31m" |
BLACK = "\x1b[30m" | ||
GREEN = "\x1b[32m" | ||
GREY = "\x1b[90m" | ||
MAGENTA = "\x1b[35m" | ||
RED = "\x1b[31m" | ||
RESET = "\x1b[0m" | ||
TEAL = "\x1b[36m" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BLACK = "\x1b[30m" | |
GREEN = "\x1b[32m" | |
GREY = "\x1b[90m" | |
MAGENTA = "\x1b[35m" | |
RED = "\x1b[31m" | |
RESET = "\x1b[0m" | |
TEAL = "\x1b[36m" | |
BLACK = "\x1b[30m" | |
CYAN = "\x1b[36m" | |
GREEN = "\x1b[32m" | |
GREY = "\x1b[90m" | |
MAGENTA = "\x1b[35m" | |
RED = "\x1b[31m" | |
RESET = "\x1b[0m" |
types.BuiltinMethodType: ANSIColors.BOLD_TEAL, | ||
types.MethodType: ANSIColors.BOLD_TEAL, | ||
type((42).__add__): ANSIColors.BOLD_TEAL, | ||
type(int.__add__): ANSIColors.BOLD_TEAL, | ||
type(str.replace): ANSIColors.BOLD_TEAL, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
types.BuiltinMethodType: ANSIColors.BOLD_TEAL, | |
types.MethodType: ANSIColors.BOLD_TEAL, | |
type((42).__add__): ANSIColors.BOLD_TEAL, | |
type(int.__add__): ANSIColors.BOLD_TEAL, | |
type(str.replace): ANSIColors.BOLD_TEAL, | |
types.BuiltinMethodType: ANSIColors.BOLD_CYAN, | |
types.MethodType: ANSIColors.BOLD_CYAN, | |
type((42).__add__): ANSIColors.BOLD_CYAN, | |
type(int.__add__): ANSIColors.BOLD_CYAN, | |
type(str.replace): ANSIColors.BOLD_CYAN, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hugo's suggestion & using types from types
types.BuiltinMethodType: ANSIColors.BOLD_TEAL, | |
types.MethodType: ANSIColors.BOLD_TEAL, | |
type((42).__add__): ANSIColors.BOLD_TEAL, | |
type(int.__add__): ANSIColors.BOLD_TEAL, | |
type(str.replace): ANSIColors.BOLD_TEAL, | |
types.BuiltinMethodType: ANSIColors.BOLD_CYAN, | |
types.MethodType: ANSIColors.BOLD_CYAN, | |
types.MethodWrapperType: ANSIColors.BOLD_CYAN, | |
types.WrapperDescriptorType: ANSIColors.BOLD_CYAN, | |
types.MethodDescriptorType: ANSIColors.BOLD_CYAN, |
|
||
type: ANSIColors.BOLD_MAGENTA, | ||
|
||
types.ModuleType: ANSIColors.TEAL, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
types.ModuleType: ANSIColors.TEAL, | |
types.ModuleType: ANSIColors.CYAN, |
# | ||
# All Rights Reserved | ||
""" | ||
Colorful TAB completion for Python prompt |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Colorful TAB completion for Python prompt | |
Colorful tab completion for Python prompt |
# this is needed to offer pyrepl a better chance to patch | ||
# raw_input. Usually, it does at import time, but is we are under | ||
# pytest with output captured, at import time we don't have a | ||
# terminal and thus the raw_input hook is not installed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# this is needed to offer pyrepl a better chance to patch | |
# raw_input. Usually, it does at import time, but is we are under | |
# pytest with output captured, at import time we don't have a | |
# terminal and thus the raw_input hook is not installed | |
# This is needed to offer PyREPL a better chance to patch | |
# raw_input. Usually, it does at import time, but if we are under | |
# pytest with output captured, at import time we don't have a | |
# terminal and thus the raw_input hook is not installed. |
# disable automatic insertion of '(' for global callables: | ||
# this method exists only in Python 2.6+ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't matter in CPython 3.14 in 2025 :)
# disable automatic insertion of '(' for global callables: | |
# this method exists only in Python 2.6+ | |
# disable automatic insertion of '(' for global callables |
# 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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# 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) | |
# 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). |
# finally, at the next TAB, we display again all the completions available | ||
# for this common prefix. Agai, we insert a spurious space to prevent the | ||
# automatic completion of ANSI sequences |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# finally, at the next TAB, we display again all the completions available | |
# for this common prefix. Agai, 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. Again, we insert a spurious space to prevent the | |
# automatic completion of ANSI sequences. |
for i, name, obj | ||
in zip(count(), names, values)] | ||
# We add a space at the end to prevent the automatic completion of the | ||
# common prefix, which is the ANSI ESCAPE sequence. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# common prefix, which is the ANSI ESCAPE sequence. | |
# common prefix, which is the ANSI escape sequence. |
# XXX: double check what happens in this case once fancycompleter works | ||
if False and hasattr(readline, '_setup'): | ||
# this is needed to offer pyrepl a better chance to patch | ||
# raw_input. Usually, it does at import time, but is we are under |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# raw_input. Usually, it does at import time, but is we are under | |
# raw_input. Usually, it does at import time, but if we are under |
return [] | ||
|
||
if len(names) == 1: | ||
return ['%s.%s' % (expr, names[0])] # only option, no coloring. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return ['%s.%s' % (expr, names[0])] # only option, no coloring. | |
return [f'{expr}.{names[0]}'] # only option, no coloring. |
|
||
prefix = commonprefix(names) | ||
if prefix and prefix != attr: | ||
return ['%s.%s' % (expr, prefix)] # autocomplete prefix |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return ['%s.%s' % (expr, prefix)] # autocomplete prefix | |
return [f'{expr}.{prefix}'] # autocomplete prefix |
for i, name, obj | ||
in zip(count(), names, values)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe? This also lets you remove the itertools
import.
for i, name, obj | |
in zip(count(), names, values)] | |
for i, (name, obj) | |
in enumerate(zip(names, values))] |
@@ -587,7 +588,12 @@ def _setup(namespace: Mapping[str, Any]) -> None: | |||
# set up namespace in rlcompleter, which requires it to be a bona fide dict | |||
if not isinstance(namespace, dict): | |||
namespace = dict(namespace) | |||
_wrapper.config.readline_completer = RLCompleter(namespace).complete | |||
|
|||
if os.getenv('PYTHON_BASIC_COMPLETER'): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this variable be documented?
def test_unicode_in___dir__(self): | ||
class Foo(object): | ||
def __dir__(self): | ||
return [u'hello', 'world'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return [u'hello', 'world'] | |
return ['hello', 'world'] |
# Copyright 2010-2025 Antonio Cuni | ||
# Daniel Hahler | ||
# | ||
# All Rights Reserved |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this have a licence grant (e.g. BSD-3-Clause)? If so, we should add it here and to the included software licences section.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another option, which might be slightly nicer, is for the contributors of the relevant bits of fancycompleter
to sign the CLA and have this work relicenced under the PSF licence. This seems to include @blueyed, @theY4Kman, @singingwolfboy, and @slinkp.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm happy to do so
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I only made this one obsolete commit to fancycompleter 13 years ago, but i'll sign if it helps :) How do I do that?
""" | ||
Colorful TAB completion for Python prompt | ||
""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
""" | |
Colorful TAB completion for Python prompt | |
""" | |
"""Colorful tab completion for the Python prompt""" | |
from _colorize import ANSIColors | ||
import rlcompleter | ||
import types | ||
from itertools import count |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can be removed with the enumerate
suggestion
from itertools import count |
types.BuiltinMethodType: ANSIColors.BOLD_TEAL, | ||
types.MethodType: ANSIColors.BOLD_TEAL, | ||
type((42).__add__): ANSIColors.BOLD_TEAL, | ||
type(int.__add__): ANSIColors.BOLD_TEAL, | ||
type(str.replace): ANSIColors.BOLD_TEAL, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hugo's suggestion & using types from types
types.BuiltinMethodType: ANSIColors.BOLD_TEAL, | |
types.MethodType: ANSIColors.BOLD_TEAL, | |
type((42).__add__): ANSIColors.BOLD_TEAL, | |
type(int.__add__): ANSIColors.BOLD_TEAL, | |
type(str.replace): ANSIColors.BOLD_TEAL, | |
types.BuiltinMethodType: ANSIColors.BOLD_CYAN, | |
types.MethodType: ANSIColors.BOLD_CYAN, | |
types.MethodWrapperType: ANSIColors.BOLD_CYAN, | |
types.WrapperDescriptorType: ANSIColors.BOLD_CYAN, | |
types.MethodDescriptorType: ANSIColors.BOLD_CYAN, |
type: ANSIColors.BOLD_MAGENTA, | ||
|
||
types.ModuleType: ANSIColors.TEAL, | ||
type(None): ANSIColors.GREY, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
type(None): ANSIColors.GREY, | |
types.NoneType: ANSIColors.GREY, |
return word | ||
|
||
def global_matches(self, text): | ||
import keyword |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is a lazy import worth it here?
words = set(dir(thisobject)) | ||
words.discard("__builtins__") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
words = set(dir(thisobject)) | |
words.discard("__builtins__") | |
words = set(dir(thisobject)) - {'__builtins__'} |
if (word[:n] == attr and | ||
not (noprefix and word[:n+1] == noprefix)): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style:
if (word[:n] == attr and | |
not (noprefix and word[:n+1] == noprefix)): | |
if ( | |
word[:n] == attr | |
and not (noprefix and word[:n+1] == noprefix) | |
): |
""" return the common prefix of all 'names' starting with 'base' | ||
""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
""" return the common prefix of all 'names' starting with 'base' | |
""" | |
"""Return the common prefix of all 'names' starting with 'base'""" |
@@ -35,6 +35,7 @@ | |||
from site import gethistoryfile # type: ignore[attr-defined] | |||
import sys | |||
from rlcompleter import Completer as RLCompleter | |||
from .fancycompleter import Completer as FancyCompleter |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sort this import below from .console
?
As the title says, this is (WIP) to integrate fancycompleter into the new REPL.