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.04k stars 1.71k forks source link

[BUG] Natively inserted ANSI escape sequence characters break alignment of `Panel()` #3349

Closed dwreeves closed 5 months ago

dwreeves commented 5 months ago

Describe the bug

A user encountered this in our repo: https://github.com/ewels/rich-click/issues/192

A simplified version of the user's issue can be seen here:

import importlib.metadata
from rich.console import Console
from rich.panel import Panel
from rich.text import Text
import click
console = Console()
print(importlib.metadata.version("rich"))

s = '''
Hello, world
\x1b[35mHello, world\x1b[0m
[magenta]Hello, world[/magenta]
'''
print({k: len(k) for k in s.split("\n")})

console.print(Panel(s))
console.print(Panel(Text.from_markup(s)))

The difference in the raw string length is 9, and the Panel is 7 characters too small:

image

plaintext:

>>> console.print(Panel(s))
╭──────────────────────────────────────────────────────────────────────────────────────────────╮
│                                                                                              │
│ Hello, world                                                                                 │
│ Hello, world                                                                          │
│ Hello, world                                                                                 │
│                                                                                              │
╰──────────────────────────────────────────────────────────────────────────────────────────────╯
>>> console.print(Panel(Text.from_markup(s)))
╭──────────────────────────────────────────────────────────────────────────────────────────────╮
│                                                                                              │
│ Hello, world                                                                                 │
│ Hello, world                                                                          │
│ Hello, world                                                                                 │
│                                                                                              │
╰──────────────────────────────────────────────────────────────────────────────────────────────╯

The expected behavior was that Rich would exclude these ANSI escape characters from the padding, even though they did not come explicitly from Rich markup.

Platform

Platform: OSX Ventura 13.6, Apple M2 Max Terminal: zsh inside PyCharm

╭───────────────────────── <class 'rich.console.Console'> ─────────────────────────╮
│ A high level console interface.                                                  │
│                                                                                  │
│ ╭──────────────────────────────────────────────────────────────────────────────╮ │
│ │ <console width=96 ColorSystem.EIGHT_BIT>                                     │ │
│ ╰──────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                  │
│     color_system = '256'                                                         │
│         encoding = 'utf-8'                                                       │
│             file = <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'> │
│           height = 47                                                            │
│    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=96, height=47),              │
│                        legacy_windows=False,                                     │
│                        min_width=1,                                              │
│                        max_width=96,                                             │
│                        is_terminal=True,                                         │
│                        encoding='utf-8',                                         │
│                        max_height=47,                                            │
│                        justify=None,                                             │
│                        overflow=None,                                            │
│                        no_wrap=False,                                            │
│                        highlight=None,                                           │
│                        markup=None,                                              │
│                        height=None                                               │
│                    )                                                             │
│            quiet = False                                                         │
│           record = False                                                         │
│         safe_box = True                                                          │
│             size = ConsoleDimensions(width=96, height=47)                        │
│        soft_wrap = False                                                         │
│           stderr = False                                                         │
│            style = None                                                          │
│         tab_size = 8                                                             │
│            width = 96                                                            │
╰──────────────────────────────────────────────────────────────────────────────────╯
╭─── <class 'rich._windows.WindowsConsoleFeatures'> ────╮
│ Windows features available.                           │
│                                                       │
│ ╭───────────────────────────────────────────────────╮ │
│ │ WindowsConsoleFeatures(vt=False, truecolor=False) │ │
│ ╰───────────────────────────────────────────────────╯ │
│                                                       │
│ truecolor = False                                     │
│        vt = False                                     │
╰───────────────────────────────────────────────────────╯
╭────── Environment Variables ───────╮
│ {                                  │
│     'TERM': 'xterm-256color',      │
│     'COLORTERM': None,             │
│     'CLICOLOR': None,              │
│     'NO_COLOR': None,              │
│     'TERM_PROGRAM': None,          │
│     'COLUMNS': None,               │
│     'LINES': None,                 │
│     'JUPYTER_COLUMNS': None,       │
│     'JUPYTER_LINES': None,         │
│     'JPY_PARENT_PID': None,        │
│     'VSCODE_VERBOSE_LOGGING': None │
│ }                                  │
╰────────────────────────────────────╯
platform="Darwin"
github-actions[bot] commented 5 months ago

We found the following entry in the FAQ which you may find helpful:

Feel free to close this issue if you found an answer in the FAQ. Otherwise, please give us a little time to review.

This is an automated reply, generated by FAQtory

willmcgugan commented 5 months ago

It's best to allow Rich to generate the escape sequences. If you can't avoid using strings with ansi codes, you can use Text.from_ansi which will convert the escape sequences in to an equivalent Text instance.

dwreeves commented 5 months ago

Ah! I did not know about that. That works! Thank you 😄

github-actions[bot] commented 5 months ago

I hope we solved your problem.

If you like using Rich, you might also enjoy Textual