Skip to content

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

Draft
wants to merge 19 commits into
base: main
Choose a base branch
from

Conversation

antocuni
Copy link
Contributor

@antocuni antocuni commented Feb 22, 2025

As the title says, this is (WIP) to integrate fancycompleter into the new REPL.

Copy link
Member

@hugovk hugovk left a 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.

Comment on lines +10 to +14
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"
Copy link
Member

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

Suggested change
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"

Comment on lines 16 to +22
BLACK = "\x1b[30m"
GREEN = "\x1b[32m"
GREY = "\x1b[90m"
MAGENTA = "\x1b[35m"
RED = "\x1b[31m"
RESET = "\x1b[0m"
TEAL = "\x1b[36m"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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"

Comment on lines +21 to +25
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,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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,

Copy link
Member

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

Suggested change
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,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
types.ModuleType: ANSIColors.TEAL,
types.ModuleType: ANSIColors.CYAN,

#
# All Rights Reserved
"""
Colorful TAB completion for Python prompt
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Colorful TAB completion for Python prompt
Colorful tab completion for Python prompt

Comment on lines +64 to +67
# 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# 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.

Comment on lines +89 to +90
# disable automatic insertion of '(' for global callables:
# this method exists only in Python 2.6+
Copy link
Member

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 :)

Suggested change
# disable automatic insertion of '(' for global callables:
# this method exists only in Python 2.6+
# disable automatic insertion of '(' for global callables

Comment on lines +150 to +153
# 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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# 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).

Comment on lines +162 to +164
# 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# 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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# 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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return ['%s.%s' % (expr, prefix)] # autocomplete prefix
return [f'{expr}.{prefix}'] # autocomplete prefix

Comment on lines +178 to +179
for i, name, obj
in zip(count(), names, values)]
Copy link
Member

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.

Suggested change
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'):
Copy link
Member

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']
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return [u'hello', 'world']
return ['hello', 'world']

Comment on lines +1 to +4
# Copyright 2010-2025 Antonio Cuni
# Daniel Hahler
#
# All Rights Reserved
Copy link
Member

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.

Copy link
Member

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.

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

Copy link

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?

Comment on lines +5 to +7
"""
Colorful TAB completion for Python prompt
"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"""
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
Copy link
Member

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

Suggested change
from itertools import count

Comment on lines +21 to +25
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,
Copy link
Member

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

Suggested change
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,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
type(None): ANSIColors.GREY,
types.NoneType: ANSIColors.GREY,

return word

def global_matches(self, text):
import keyword
Copy link
Member

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?

Comment on lines +125 to +126
words = set(dir(thisobject))
words.discard("__builtins__")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
words = set(dir(thisobject))
words.discard("__builtins__")
words = set(dir(thisobject)) - {'__builtins__'}

Comment on lines +143 to +144
if (word[:n] == attr and
not (noprefix and word[:n+1] == noprefix)):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style:

Suggested change
if (word[:n] == attr and
not (noprefix and word[:n+1] == noprefix)):
if (
word[:n] == attr
and not (noprefix and word[:n+1] == noprefix)
):

Comment on lines +194 to +195
""" return the common prefix of all 'names' starting with 'base'
"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
""" 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
Copy link
Member

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants