Skip to content
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

✨ Add option to set the width of the Rich exception output #528

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
11 changes: 11 additions & 0 deletions docs/tutorial/exceptions.md
Expand Up @@ -202,6 +202,17 @@ $ python main.py

</div>


## Set Output Width

If you want to control the width of the yellow and red Rich exception borders, you can set the parameter `pretty_exceptions_width` to a specific integer (it's 100 by default):

```Python hl_lines="3"
{!../docs_src/exceptions/tutorial005.py!}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Alternatively, should we rename tutorial004 to 005 and call the new one from this PR 004?

```

This prevents artificial line breaks in cases where there is sufficient horizontal space on the console.

Copy link
Collaborator

@svlandeg svlandeg Apr 18, 2024

Choose a reason for hiding this comment

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

Tiangolo: I'm not sure whether we want to show output here in a <div class="termy">, it gets a bit unwieldy.

## Disable Pretty Exceptions

You can also entirely disable pretty exceptions with the parameter `pretty_exceptions_enable=False`:
Expand Down
39 changes: 39 additions & 0 deletions docs_src/exceptions/tutorial005.py
@@ -0,0 +1,39 @@
import typer

app = typer.Typer(pretty_exceptions_width=120)


@app.command()
def main(name: str = "morty"):
deep_dict_or_json = {
"this_is_a_long_key": {
"this_is_the_next_long_key": {
"this_is_the_next_long_key": {
"this_is_the_next_long_key": {
"this_is_the_next_long_key": {
"this_is_the_next_long_key": {
"this_is_the_next_long_key": {
"this_is_the_next_long_key": {
"this_is_the_next_long_key": {
"this_is_the_next_long_key": {
"and_once_again_a_very_long_key": {
"but_this_is_not_the_end": {
"end": True
}
}
}
}
}
}
}
}
}
}
}
}
}
print(name + deep_dict_or_json + 3)


if __name__ == "__main__":
app()
34 changes: 34 additions & 0 deletions tests/test_tutorial/test_exceptions/test_tutorial005.py
@@ -0,0 +1,34 @@
import os
import subprocess
import sys
from pathlib import Path

from typer.testing import CliRunner

from docs_src.exceptions import tutorial005 as mod

runner = CliRunner()


def test_traceback_rich_pretty_width():
Copy link
Collaborator

Choose a reason for hiding this comment

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

This test simply checks that the program runs like expected, but it doesn't (yet) actually test the Rich borders length as I wasn't quite sure how to do that 🤔

file_path = Path(mod.__file__)
result = subprocess.run(
[sys.executable, "-m", "coverage", "run", str(file_path)],
capture_output=True,
encoding="utf-8",
env={**os.environ, "_TYPER_STANDARD_TRACEBACK": ""},
)
print(result.stderr)

assert "print(name + deep_dict_or_json + 3)" in result.stderr
assert 'TypeError: can only concatenate str (not "dict") to str' in result.stderr
assert "name = 'morty'" in result.stderr


def test_script():
result = subprocess.run(
[sys.executable, "-m", "coverage", "run", mod.__file__, "--help"],
capture_output=True,
encoding="utf-8",
)
assert "Usage" in result.stdout
4 changes: 4 additions & 0 deletions typer/main.py
Expand Up @@ -75,6 +75,7 @@ def except_hook(
exc.__traceback__,
show_locals=exception_config.pretty_exceptions_show_locals,
suppress=supress_internal_dir_names,
width=exception_config.pretty_exceptions_width,
)
console_stderr.print(rich_tb)
return
Expand Down Expand Up @@ -137,13 +138,15 @@ def __init__(
pretty_exceptions_enable: bool = True,
pretty_exceptions_show_locals: bool = True,
pretty_exceptions_short: bool = True,
pretty_exceptions_width: int = 100,
):
self._add_completion = add_completion
self.rich_markup_mode: MarkupMode = rich_markup_mode
self.rich_help_panel = rich_help_panel
self.pretty_exceptions_enable = pretty_exceptions_enable
self.pretty_exceptions_show_locals = pretty_exceptions_show_locals
self.pretty_exceptions_short = pretty_exceptions_short
self.pretty_exceptions_width = pretty_exceptions_width
self.info = TyperInfo(
name=name,
cls=cls,
Expand Down Expand Up @@ -321,6 +324,7 @@ def __call__(self, *args: Any, **kwargs: Any) -> Any:
pretty_exceptions_enable=self.pretty_exceptions_enable,
pretty_exceptions_show_locals=self.pretty_exceptions_show_locals,
pretty_exceptions_short=self.pretty_exceptions_short,
pretty_exceptions_width=self.pretty_exceptions_width,
),
)
raise e
Expand Down
2 changes: 2 additions & 0 deletions typer/models.py
Expand Up @@ -513,7 +513,9 @@ def __init__(
pretty_exceptions_enable: bool = True,
pretty_exceptions_show_locals: bool = True,
pretty_exceptions_short: bool = True,
pretty_exceptions_width: int = 100,
) -> None:
self.pretty_exceptions_enable = pretty_exceptions_enable
self.pretty_exceptions_show_locals = pretty_exceptions_show_locals
self.pretty_exceptions_short = pretty_exceptions_short
self.pretty_exceptions_width = pretty_exceptions_width