Skip to content

Commit ec5d2f1

Browse files
committed
Merge branch 'master' into exception-groups
2 parents 13f9b4f + 2ffd9d6 commit ec5d2f1

File tree

3 files changed

+55
-22
lines changed

3 files changed

+55
-22
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
## Unreleased
99

1010
### Added
11+
1112
- Added env var `TTY_COMPATIBLE` to override auto-detection of TTY support (See console.rst for details). https://github.com/Textualize/rich/pull/3675
1213

1314
### Changed
1415

1516
- An empty `NO_COLOR` env var is now considered disabled. https://github.com/Textualize/rich/pull/3675
1617
- An empty `FORCE_COLOR` env var is now considered disabled. https://github.com/Textualize/rich/pull/3675
1718
- Rich tracebacks will now render notes on Python 3.11 onwards (added with `Exception.add_note`) https://github.com/Textualize/rich/pull/3676
18-
19+
- Indentation in exceptions won't be underlined https://github.com/Textualize/rich/pull/3678
1920

2021
## [13.9.4] - 2024-11-01
2122

rich/default_styles.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@
120120
"traceback.exc_type": Style(color="bright_red", bold=True),
121121
"traceback.exc_value": Style.null(),
122122
"traceback.offset": Style(color="bright_red", bold=True),
123-
"traceback.error_range": Style(underline=True, bold=True, dim=False),
123+
"traceback.error_range": Style(underline=True, bold=True),
124124
"traceback.note": Style(color="green", bold=True),
125125
"traceback.group.border": Style(color="magenta"),
126126
"bar.back": Style(color="grey23"),

rich/traceback.py

Lines changed: 52 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from pygments.util import ClassNotFound
2727

2828
from . import pretty
29-
from ._loop import loop_last
29+
from ._loop import loop_first_last, loop_last
3030
from .columns import Columns
3131
from .console import (
3232
Console,
@@ -41,7 +41,7 @@
4141
from .panel import Panel
4242
from .scope import render_scope
4343
from .style import Style
44-
from .syntax import Syntax
44+
from .syntax import Syntax, SyntaxPosition
4545
from .text import Text
4646
from .theme import Theme
4747

@@ -51,6 +51,34 @@
5151
LOCALS_MAX_STRING = 80
5252

5353

54+
def _iter_syntax_lines(
55+
start: SyntaxPosition, end: SyntaxPosition
56+
) -> Iterable[Tuple[int, int, int]]:
57+
"""Yield start and end positions per line.
58+
59+
Args:
60+
start: Start position.
61+
end: End position.
62+
63+
Returns:
64+
Iterable of (LINE, COLUMN1, COLUMN2).
65+
"""
66+
67+
line1, column1 = start
68+
line2, column2 = end
69+
70+
if line1 == line2:
71+
yield line1, column1, column2
72+
else:
73+
for first, last, line_no in loop_first_last(range(line1, line2 + 1)):
74+
if first:
75+
yield line_no, column1, -1
76+
elif last:
77+
yield line_no, 0, column2
78+
else:
79+
yield line_no, 0, -1
80+
81+
5482
def install(
5583
*,
5684
console: Optional[Console] = None,
@@ -703,17 +731,6 @@ def _render_stack(self, stack: Stack) -> RenderResult:
703731
path_highlighter = PathHighlighter()
704732
theme = self.theme
705733

706-
def read_code(filename: str) -> str:
707-
"""Read files, and cache results on filename.
708-
709-
Args:
710-
filename (str): Filename to read
711-
712-
Returns:
713-
str: Contents of file
714-
"""
715-
return "".join(linecache.getlines(filename))
716-
717734
def render_locals(frame: Frame) -> Iterable[ConsoleRenderable]:
718735
if frame.locals:
719736
yield render_scope(
@@ -775,7 +792,8 @@ def render_locals(frame: Frame) -> Iterable[ConsoleRenderable]:
775792
continue
776793
if not suppressed:
777794
try:
778-
code = read_code(frame.filename)
795+
code_lines = linecache.getlines(frame.filename)
796+
code = "".join(code_lines)
779797
if not code:
780798
# code may be an empty string if the file doesn't exist, OR
781799
# if the traceback filename is generated dynamically
@@ -804,12 +822,26 @@ def render_locals(frame: Frame) -> Iterable[ConsoleRenderable]:
804822
else:
805823
if frame.last_instruction is not None:
806824
start, end = frame.last_instruction
807-
syntax.stylize_range(
808-
style="traceback.error_range",
809-
start=start,
810-
end=end,
811-
style_before=True,
812-
)
825+
826+
# Stylize a line at a time
827+
# So that indentation isn't underlined (which looks bad)
828+
for line1, column1, column2 in _iter_syntax_lines(start, end):
829+
try:
830+
if column1 == 0:
831+
line = code_lines[line1 - 1]
832+
column1 = len(line) - len(line.lstrip())
833+
if column2 == -1:
834+
column2 = len(code_lines[line1 - 1])
835+
except IndexError:
836+
# Being defensive here
837+
# If last_instruction reports a line out-of-bounds, we don't want to crash
838+
continue
839+
840+
syntax.stylize_range(
841+
style="traceback.error_range",
842+
start=(line1, column1),
843+
end=(line1, column2),
844+
)
813845
yield (
814846
Columns(
815847
[

0 commit comments

Comments
 (0)