Skip to content

Conversation

mahimairaja
Copy link

Problem

When using typer.style() in help text for CLI options, the help table columns become misaligned. This happens because typer.style() returns a string with ANSI escape codes, and Rich's width calculation includes these control codes, causing incorrect column sizing.

Relevant issue - #1159

Root Cause:

The issue occurs in _make_rich_text() function in rich_utils.py. When help text contains ANSI escape codes from typer.style(), the function was creating a plain Text object that preserved the escape codes as literal characters. Rich's width measurement then counted these control codes, causing misalignment.

Sample Code to reproduce:

from typing import Annotated

import typer

app = typer.Typer()


@app.command()
def example(
    a: Annotated[str, typer.Option(help="This is A")],
    b: Annotated[str, typer.Option(help=f"This is {typer.style('B', underline=True)}")],
):
    pass


if __name__ == "__main__":
    app()

Before fix:

❯ python main.py --help
                                                                                                               
 Usage: main.py [OPTIONS]                                                                                      
                                                                                                               
╭─ Options ───────────────────────────────────────────────────────────────────────────────────────────────────╮
│ *  --a                         TEXT  This is A [required]                                                   │
│ *  --b                         TEXT  This is B [required]                                             │
│    --install-completion              Install completion for the current shell.                              │
│    --show-completion                 Show completion for the current shell, to copy it or customize the     │
│                                      installation.                                                          │
│    --help                            Show this message and exit.                                            │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

After fix:

❯ python main.py --help
                                                                                                               
 Usage: main.py [OPTIONS]                                                                                      
                                                                                                               
╭─ Options ───────────────────────────────────────────────────────────────────────────────────────────────────╮
│ *  --a                         TEXT  This is A [required]                                                   │
│ *  --b                         TEXT  This is B [required]                                                   │
│    --install-completion              Install completion for the current shell.                              │
│    --show-completion                 Show completion for the current shell, to copy it or customize the     │
│                                      installation.                                                          │
│    --help                            Show this message and exit.                                            │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

Testing

  • Verified with the provided example showing proper alignment
  • Tested with various typer.style() combinations
  • Confirmed no regression in existing functionality

@svlandeg svlandeg added the bug Something isn't working label Sep 26, 2025
@svlandeg svlandeg self-assigned this Sep 26, 2025
@svlandeg svlandeg linked an issue Sep 26, 2025 that may be closed by this pull request
7 tasks
@svlandeg svlandeg changed the title Fix --help text alignment when using typer.style() in option descriptions 🐛 Fix --help text alignment when using typer.style() in option descriptions Sep 26, 2025
@svlandeg svlandeg changed the title 🐛 Fix --help text alignment when using typer.style() in option descriptions 🐛 Fix --help text alignment when using typer.style() in option descriptions Sep 26, 2025
Copy link
Member

@svlandeg svlandeg left a comment

Choose a reason for hiding this comment

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

Thanks for the PR @mahimairaja, nice work!

I don't think we had originally envisioned typer.style() to be used as part of the help text of an option, but I can see how this may come up.

On my console, the problem is actually worse when printing the help of the example code on master, I think because it doesn't fully support all formatting. I get this:

image

With this PR, that becomes

image

@mahimairaja: can you share a screenshot of the output on your console with this PR? I'd like to see whether the underlined behaviour is in fact displayed correctly on a console that supports it.


Mostly, I think this PR looks good. Just a few thoughts/considerations:

  • I don't particularly like hard-coding "\x1b[" in rich_utils.py. Is there a nicer way to check for the ansi escape code? Also I'd like to point out that click actually uses "\033[" in https://github.com/pallets/click/blob/main/src/click/termui.py#L512. While they are the same, it might make sense to adhere to the same literal string just for clarity.
  • We could also consider not having the if-else check on ANSI_ESCAPE_SEQUENCE and just run Text.from_ansi always, but maybe that's computationally unnecessarily expensive for non-escaped strings 🤔
  • Finally, while the tests look good, I was wondering if we could add a check for the actual right boundary of "|" . As this was what the report focused on initially, the boundary being misaligned. We could do something like check how many (empty) characters inbetween "This is A" and the next "|"` and then again on the line for B. Maybe it's a bit of a convoluted test, but it would check the correct behaviour.

@svlandeg svlandeg removed their assignment Oct 3, 2025
@mahimairaja
Copy link
Author

Hi @svlandeg

Here is the screenshot before fix:
Screenshot 2025-10-04 at 5 54 20 AM

after fix:
Screenshot 2025-10-04 at 5 53 06 AM

Thanks for the guidance,

  1. I have updated the ansi prefix as similar to click
  2. Yes, @svlandeg let's have a if condition, if we are passing all plain text along with ansi text, it would increase time complexity.
  3. Added the test case for | based on the comparing all three lines as you mentioned.

Can you please review the changes.

@mahimairaja mahimairaja requested a review from svlandeg October 4, 2025 10:47
@svlandeg svlandeg self-assigned this Oct 6, 2025
Copy link
Member

@svlandeg svlandeg left a comment

Choose a reason for hiding this comment

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

This looks good to merge to me, I'll pass it by Tiangolo for a final check.

Thanks again, @mahimairaja!

@svlandeg svlandeg removed their assignment Oct 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] help line length miscalculated when using stylized text

2 participants