diff --git a/Lib/_pyrepl/utils.py b/Lib/_pyrepl/utils.py index fd788c8429e15b..c5d006afa7731f 100644 --- a/Lib/_pyrepl/utils.py +++ b/Lib/_pyrepl/utils.py @@ -208,7 +208,10 @@ def gen_colors_from_token_stream( ): span = Span.from_token(token, line_lengths) yield ColorSpan(span, "soft_keyword") - elif token.string in BUILTINS: + elif ( + token.string in BUILTINS + and not (prev_token and prev_token.exact_type == T.DOT) + ): span = Span.from_token(token, line_lengths) yield ColorSpan(span, "builtin") diff --git a/Lib/test/test_pyrepl/test_utils.py b/Lib/test/test_pyrepl/test_utils.py index 8ce1e5371386f0..05a4f329059835 100644 --- a/Lib/test/test_pyrepl/test_utils.py +++ b/Lib/test/test_pyrepl/test_utils.py @@ -1,6 +1,6 @@ from unittest import TestCase -from _pyrepl.utils import str_width, wlen, prev_next_window +from _pyrepl.utils import str_width, wlen, prev_next_window, gen_colors class TestUtils(TestCase): @@ -60,3 +60,25 @@ def gen_raise(): self.assertEqual(next(pnw), (3, 4, None)) with self.assertRaises(ZeroDivisionError): next(pnw) + + def test_gen_colors_keyword_highlighting(self): + cases = [ + # no highlights + ("a.set", [(".", "op")]), + ("obj.list", [(".", "op")]), + ("obj.match", [(".", "op")]), + ("b. \\\n format", [(".", "op")]), + # highlights + ("set", [("set", "builtin")]), + ("list", [("list", "builtin")]), + (" \n dict", [("dict", "builtin")]), + ] + for code, expected_highlights in cases: + with self.subTest(code=code): + colors = list(gen_colors(code)) + # Extract (text, tag) pairs for comparison + actual_highlights = [] + for color in colors: + span_text = code[color.span.start:color.span.end + 1] + actual_highlights.append((span_text, color.tag)) + self.assertEqual(actual_highlights, expected_highlights) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-09-01-16-09-02.gh-issue-138318.t-WEN5.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-09-01-16-09-02.gh-issue-138318.t-WEN5.rst new file mode 100644 index 00000000000000..ce9456ddd10954 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-09-01-16-09-02.gh-issue-138318.t-WEN5.rst @@ -0,0 +1,3 @@ +The default REPL now avoids highlighting built-in names (for instance :class:`set` +or :func:`format`) when they are used as attribute names (for instance in ``value.set`` +or ``text.format``).