From f19b246826d23ec14ee09edeedc5953c640792ed Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 28 Mar 2025 13:49:49 +0000 Subject: [PATCH 1/8] render notes in traceback --- rich/default_styles.py | 1 + rich/traceback.py | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/rich/default_styles.py b/rich/default_styles.py index 28e8f6f94..3975a3615 100644 --- a/rich/default_styles.py +++ b/rich/default_styles.py @@ -121,6 +121,7 @@ "traceback.exc_value": Style.null(), "traceback.offset": Style(color="bright_red", bold=True), "traceback.error_range": Style(underline=True, bold=True, dim=False), + "traceback.note": Style(color="green", bold=True), "bar.back": Style(color="grey23"), "bar.complete": Style(color="rgb(249,38,114)"), "bar.finished": Style(color="rgb(114,156,31)"), diff --git a/rich/traceback.py b/rich/traceback.py index 3bf5baa7b..a7090c681 100644 --- a/rich/traceback.py +++ b/rich/traceback.py @@ -191,6 +191,7 @@ class _SyntaxError: line: str lineno: int msg: str + notes: List[str] = field(default_factory=list) @dataclass @@ -200,6 +201,7 @@ class Stack: syntax_error: Optional[_SyntaxError] = None is_cause: bool = False frames: List[Frame] = field(default_factory=list) + notes: List[str] = field(default_factory=list) @dataclass @@ -403,6 +405,8 @@ def extract( from rich import _IMPORT_CWD + notes: list[str] = getattr(exc_value, "__notes__", None) or [] + def safe_str(_object: Any) -> str: """Don't allow exceptions from __str__ to propagate.""" try: @@ -415,6 +419,7 @@ def safe_str(_object: Any) -> str: exc_type=safe_str(exc_type.__name__), exc_value=safe_str(exc_value), is_cause=is_cause, + notes=notes, ) if isinstance(exc_value, SyntaxError): @@ -424,13 +429,14 @@ def safe_str(_object: Any) -> str: lineno=exc_value.lineno or 0, line=exc_value.text or "", msg=exc_value.msg, + notes=notes, ) stacks.append(stack) append = stack.frames.append def get_locals( - iter_locals: Iterable[Tuple[str, object]] + iter_locals: Iterable[Tuple[str, object]], ) -> Iterable[Tuple[str, object]]: """Extract locals from an iterator of key pairs.""" if not (locals_hide_dunder or locals_hide_sunder): @@ -569,6 +575,7 @@ def __rich_console__( stack_renderable = Constrain(stack_renderable, self.width) with console.use_theme(traceback_theme): yield stack_renderable + if stack.syntax_error is not None: with console.use_theme(traceback_theme): yield Constrain( @@ -594,6 +601,13 @@ def __rich_console__( else: yield Text.assemble((f"{stack.exc_type}", "traceback.exc_type")) + if stack.notes: + for note in stack.notes: + yield Text.assemble( + Text.from_markup("[NOTE] ", style="traceback.note"), + highlighter(note), + ) + if not last: if stack.is_cause: yield Text.from_markup( From 9dce0645441a08ad23171422ffc91b040cf6c840 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 28 Mar 2025 13:50:29 +0000 Subject: [PATCH 2/8] changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c7332fee..ec9c2241e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Added + +- Rich tracebacks will no render notes (added with Exception.add_note) ## [13.9.4] - 2024-11-01 From 580cfe85d36a6cd808c03918e2e3563d2cf2f41b Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 28 Mar 2025 13:51:53 +0000 Subject: [PATCH 3/8] changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec9c2241e..ddfc0f62b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Rich tracebacks will no render notes (added with Exception.add_note) +- Rich tracebacks will no render notes (added with Exception.add_note) https://github.com/Textualize/rich/pull/3676 ## [13.9.4] - 2024-11-01 From 44797c00ada042f3d0c42c059361e6d0ccd2b528 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 28 Mar 2025 14:07:57 +0000 Subject: [PATCH 4/8] added trest --- rich/traceback.py | 3 +-- tests/test_traceback.py | 12 ++++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/rich/traceback.py b/rich/traceback.py index a7090c681..e9b5797ce 100644 --- a/rich/traceback.py +++ b/rich/traceback.py @@ -604,8 +604,7 @@ def __rich_console__( if stack.notes: for note in stack.notes: yield Text.assemble( - Text.from_markup("[NOTE] ", style="traceback.note"), - highlighter(note), + ("[NOTE] ", "traceback.note"), highlighter(note) ) if not last: diff --git a/tests/test_traceback.py b/tests/test_traceback.py index ed80d1ba7..493c7d268 100644 --- a/tests/test_traceback.py +++ b/tests/test_traceback.py @@ -358,3 +358,15 @@ def test_traceback_finely_grained() -> None: start, end = last_instruction print(start, end) assert start[0] == end[0] + + +def test_notes() -> None: + """Check traceback captures __note__.""" + try: + 1 / 0 + except Exception as error: + error.add_note("Hello") + error.add_note("World") + traceback = Traceback() + + assert traceback.trace.stacks[0].notes == ["Hello", "World"] From 43d07993d9cf1a78e52395a31fa1398e626a92d1 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 28 Mar 2025 14:15:49 +0000 Subject: [PATCH 5/8] simplify --- rich/traceback.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/rich/traceback.py b/rich/traceback.py index e9b5797ce..292b530f1 100644 --- a/rich/traceback.py +++ b/rich/traceback.py @@ -601,11 +601,8 @@ def __rich_console__( else: yield Text.assemble((f"{stack.exc_type}", "traceback.exc_type")) - if stack.notes: - for note in stack.notes: - yield Text.assemble( - ("[NOTE] ", "traceback.note"), highlighter(note) - ) + for note in stack.notes: + yield Text.assemble(("[NOTE] ", "traceback.note"), highlighter(note)) if not last: if stack.is_cause: From d09e63afe173110a83e2c957234ea869290c1c94 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 28 Mar 2025 14:18:14 +0000 Subject: [PATCH 6/8] fix typing --- rich/traceback.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rich/traceback.py b/rich/traceback.py index 292b530f1..fc9859b93 100644 --- a/rich/traceback.py +++ b/rich/traceback.py @@ -405,7 +405,7 @@ def extract( from rich import _IMPORT_CWD - notes: list[str] = getattr(exc_value, "__notes__", None) or [] + notes: List[str] = getattr(exc_value, "__notes__", None) or [] def safe_str(_object: Any) -> str: """Don't allow exceptions from __str__ to propagate.""" From 35b0f52cbf32058403e279b5a51c9735210bc03b Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 28 Mar 2025 14:32:22 +0000 Subject: [PATCH 7/8] notes added in 3.11 --- tests/test_traceback.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_traceback.py b/tests/test_traceback.py index 493c7d268..bc9bc91e9 100644 --- a/tests/test_traceback.py +++ b/tests/test_traceback.py @@ -360,6 +360,9 @@ def test_traceback_finely_grained() -> None: assert start[0] == end[0] +@pytest.mark.skipif( + sys.version_info.minor < 11, reason="Not supported before Python 3.11" +) def test_notes() -> None: """Check traceback captures __note__.""" try: From 17baaed1c0525f23dca438b8192af79cb52b792e Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 28 Mar 2025 14:37:05 +0000 Subject: [PATCH 8/8] changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ddfc0f62b..27a6fc01e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Rich tracebacks will no render notes (added with Exception.add_note) https://github.com/Textualize/rich/pull/3676 +- Rich tracebacks will now render notes on Python 3.11 onwards (added with `Exception.add_note`) https://github.com/Textualize/rich/pull/3676 ## [13.9.4] - 2024-11-01