Textualize / rich

Rich is a Python library for rich text and beautiful formatting in the terminal.
https://rich.readthedocs.io/en/latest/
MIT License
49.48k stars 1.72k forks source link

[BUG] Colors break if there's an `<` in the repr #3024

Open MicaelJarniac opened 1 year ago

MicaelJarniac commented 1 year ago

Describe the bug

If there's a < (less than) char in the repr of an object, the colors of other components that use <> break after it.

from rich.pretty import pprint

class A:
    def __init__(self, a=None):
        self.a = a

    def __repr__(self):
        return f"A(a={self.a!r}, b=<B>)"

pprint(A())
pprint(A("<"))
pprint(A(">"))
pprint(A("x"))

image

For example, if I have a dataclass or an attrs class with more than one enum attribute, the color of subsequent ones break when pretty printing.

from dataclasses import dataclass
from enum import Enum, auto

import attrs
from rich.pretty import pprint

class MyEnum(Enum):
    ONE = auto()

@attrs.define  # or @dataclass
class MyClass:
    first: MyEnum | None = None
    second: MyEnum | None = None

pprint(MyClass())
pprint(MyClass(first=MyEnum.ONE))
pprint(MyClass(second=MyEnum.ONE))
pprint(MyClass(first=MyEnum.ONE, second=MyEnum.ONE))

On the terminal: image

On Jupyter: image

Platform

Click to expand Operating System: Manjaro Linux KDE Plasma Version: 5.27.5 KDE Frameworks Version: 5.106.0 Qt Version: 5.15.9 Kernel Version: 6.1.31-2-MANJARO (64-bit) Graphics Platform: Wayland Processors: 16 × AMD Ryzen 7 5800H with Radeon Graphics Memory: 13.5 GiB of RAM Graphics Processor: AMD Radeon Graphics Manufacturer: LENOVO Product Name: 82MJ System Version: IdeaPad Gaming 3 15ACH6 Shell: Zsh with Zsh for Humans and Tmux Terminal: VS Code's built-in terminal If you're using Rich in a terminal: ``` python -m rich.diagnose pip freeze | grep rich ```
`python -m rich.diagnose` ``` ❯ python -m rich.diagnose ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ A high level console interface. │ │ │ │ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ │ │ │ │ │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │ │ │ │ color_system = 'truecolor' │ │ encoding = 'utf-8' │ │ file = <_io.TextIOWrapper name='' mode='w' encoding='utf-8'> │ │ height = 18 │ │ is_alt_screen = False │ │ is_dumb_terminal = False │ │ is_interactive = True │ │ is_jupyter = False │ │ is_terminal = True │ │ legacy_windows = False │ │ no_color = False │ │ options = ConsoleOptions(size=ConsoleDimensions(width=271, height=18), legacy_windows=False, min_width=1, max_width=271, is_terminal=True, encoding='utf-8', max_height=18, justify=None, overflow=None, no_wrap=False, highlight=None, markup=None, height=None) │ │ quiet = False │ │ record = False │ │ safe_box = True │ │ size = ConsoleDimensions(width=271, height=18) │ │ soft_wrap = False │ │ stderr = False │ │ style = None │ │ tab_size = 8 │ │ width = 271 │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─── ────╮ │ Windows features available. │ │ │ │ ╭───────────────────────────────────────────────────╮ │ │ │ WindowsConsoleFeatures(vt=False, truecolor=False) │ │ │ ╰───────────────────────────────────────────────────╯ │ │ │ │ truecolor = False │ │ vt = False │ ╰───────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────── Environment Variables ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ {'TERM': 'tmux-256color', 'COLORTERM': 'truecolor', 'CLICOLOR': None, 'NO_COLOR': None, 'TERM_PROGRAM': 'tmux', 'COLUMNS': None, 'LINES': None, 'JUPYTER_COLUMNS': None, 'JUPYTER_LINES': None, 'JPY_PARENT_PID': None, 'VSCODE_VERBOSE_LOGGING': None} │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ platform="Linux" ```
``` ❯ pip freeze | grep rich rich==13.4.2 ❯ pip freeze | grep attrs attrs==23.1.0 ``` If you're using Rich in a Jupyter Notebook, run the following snippet in a cell and paste the output in your bug report. ```python from rich.diagnose import report report() ```
Report ``` ╭────────────────────── ──────────────────────╮ │ A high level console interface. │ │ │ │ ╭────────────────────────────────────────────────────────────────────────╮ │ │ │ │ │ │ ╰────────────────────────────────────────────────────────────────────────╯ │ │ │ │ color_system = 'truecolor' │ │ encoding = 'utf-8' │ │ file = │ │ height = 100 │ │ is_alt_screen = False │ │ is_dumb_terminal = False │ │ is_interactive = False │ │ is_jupyter = True │ │ is_terminal = False │ │ legacy_windows = False │ │ no_color = False │ │ options = ConsoleOptions( │ │ size=ConsoleDimensions(width=115, height=100), │ │ legacy_windows=False, │ │ min_width=1, │ │ max_width=115, │ │ is_terminal=False, │ │ encoding='utf-8', │ │ max_height=100, │ │ justify=None, │ │ overflow=None, │ │ no_wrap=False, │ │ highlight=None, │ │ markup=None, │ │ height=None │ │ ) │ │ quiet = False │ │ record = False │ │ safe_box = True │ │ size = ConsoleDimensions(width=115, height=100) │ │ soft_wrap = False │ │ stderr = False │ │ style = None │ │ tab_size = 8 │ │ width = 115 │ ╰────────────────────────────────────────────────────────────────────────────╯ ╭─── ────╮ │ Windows features available. │ │ │ │ ╭───────────────────────────────────────────────────╮ │ │ │ WindowsConsoleFeatures(vt=False, truecolor=False) │ │ │ ╰───────────────────────────────────────────────────╯ │ │ │ │ truecolor = False │ │ vt = False │ ╰───────────────────────────────────────────────────────╯ ╭────── Environment Variables ───────╮ │ { │ │ 'TERM': 'xterm-color', │ │ 'COLORTERM': 'truecolor', │ │ 'CLICOLOR': '1', │ │ 'NO_COLOR': None, │ │ 'TERM_PROGRAM': 'tmux', │ │ 'COLUMNS': None, │ │ 'LINES': None, │ │ 'JUPYTER_COLUMNS': None, │ │ 'JUPYTER_LINES': None, │ │ 'JPY_PARENT_PID': None, │ │ 'VSCODE_VERBOSE_LOGGING': None │ │ } │ ╰────────────────────────────────────╯ platform="Linux" ```
github-actions[bot] commented 1 year ago

Thank you for your issue. Give us a little time to review it.

PS. You might want to check the FAQ if you haven't done so already.

This is an automated reply, generated by FAQtory

trag1c commented 1 year ago

Same thing happens on macOS Ventura 13.4.1 with iTerm2. Doesn't seem like it's platform-dependent but @MicaelJarniac asked me to check :D

62747

MicaelJarniac commented 1 year ago

I've simplified the example a lot, and came to this:

from rich.pretty import pprint

class A:
    def __init__(self, a=None):
        self.a = a

    def __repr__(self) -> str:
        return f"A(a={repr(self.a)}, b=<B>)"

pprint(A())
pprint(A("<"))
pprint(A(">"))
pprint(A("x"))

image

MicaelJarniac commented 1 year ago

I suspect the bug is in this line: https://github.com/Textualize/rich/blob/aca9467ec6a482b3bed1ba7d2e3de72271c5f817/rich/highlighter.py#L85

MicaelJarniac commented 1 year ago
from rich.pretty import pprint

class A:
    def __init__(self, a=None):
        self.a = a

    def __repr__(self) -> str:
        return f"A(a={self.a!r}, b=1.23, c=None, d=(1, 2))"

pprint([A("<"), A(">")])

image