mirror of
https://github.com/Textualize/rich.git
synced 2025-08-04 10:08:40 +00:00
Use the proper error message with caused exceptions
When an exception is raised using "from", the chained exception goes into __cause__, which results in a different message using Python's native traceback handler. This patch properly replicates that error message.
This commit is contained in:
parent
6cff61ffbb
commit
a6b337954f
3 changed files with 56 additions and 6 deletions
|
@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
- Fixed redirecting of stderr in Progress
|
||||
- Fixed broken expanded tuple of one https://github.com/willmcgugan/rich/issues/445
|
||||
- Fixed traceback message with `from` exceptions
|
||||
|
||||
## [9.2.0] - 2020-11-08
|
||||
|
||||
|
|
|
@ -121,6 +121,7 @@ class Stack:
|
|||
exc_type: str
|
||||
exc_value: str
|
||||
syntax_error: Optional[_SyntaxError] = None
|
||||
is_cause: bool = False
|
||||
frames: List[Frame] = field(default_factory=list)
|
||||
|
||||
|
||||
|
@ -237,8 +238,13 @@ class Traceback:
|
|||
"""
|
||||
|
||||
stacks: List[Stack] = []
|
||||
is_cause = False
|
||||
while True:
|
||||
stack = Stack(exc_type=str(exc_type.__name__), exc_value=str(exc_value))
|
||||
stack = Stack(
|
||||
exc_type=str(exc_type.__name__),
|
||||
exc_value=str(exc_value),
|
||||
is_cause=is_cause,
|
||||
)
|
||||
|
||||
if isinstance(exc_value, SyntaxError):
|
||||
stack.syntax_error = _SyntaxError(
|
||||
|
@ -268,12 +274,22 @@ class Traceback:
|
|||
)
|
||||
append(frame)
|
||||
|
||||
cause = getattr(exc_value, "__cause__", None)
|
||||
if cause and cause.__traceback__:
|
||||
exc_type = cause.__class__
|
||||
exc_value = cause
|
||||
traceback = cause.__traceback__
|
||||
if traceback:
|
||||
is_cause = True
|
||||
continue
|
||||
|
||||
cause = exc_value.__context__
|
||||
if cause and cause.__traceback__:
|
||||
exc_type = cause.__class__
|
||||
exc_value = cause
|
||||
traceback = cause.__traceback__
|
||||
if traceback:
|
||||
is_cause = False
|
||||
continue
|
||||
# No cover, code is reached but coverage doesn't recognize it.
|
||||
break # pragma: no cover
|
||||
|
@ -347,9 +363,14 @@ class Traceback:
|
|||
)
|
||||
|
||||
if not last:
|
||||
yield Text.from_markup(
|
||||
"\n[i]During handling of the above exception, another exception occurred:\n",
|
||||
)
|
||||
if stack.is_cause:
|
||||
yield Text.from_markup(
|
||||
"\n[i]The above exception was the direct cause of the following exception:\n",
|
||||
)
|
||||
else:
|
||||
yield Text.from_markup(
|
||||
"\n[i]During handling of the above exception, another exception occurred:\n",
|
||||
)
|
||||
|
||||
@render_group()
|
||||
def _render_syntax_error(self, syntax_error: _SyntaxError) -> RenderResult:
|
||||
|
|
|
@ -94,7 +94,7 @@ def test_syntax_error():
|
|||
|
||||
def test_nested_exception():
|
||||
console = Console(width=100, file=io.StringIO())
|
||||
value_error_message = "ValueError because of ZeroDivisionEerror"
|
||||
value_error_message = "ValueError because of ZeroDivisionError"
|
||||
|
||||
try:
|
||||
try:
|
||||
|
@ -112,7 +112,35 @@ def test_nested_exception():
|
|||
"During handling of the above exception",
|
||||
]
|
||||
|
||||
assert [msg in exception_text for msg in text_should_contain]
|
||||
for msg in text_should_contain:
|
||||
assert msg in exception_text
|
||||
|
||||
# ZeroDivisionError should come before ValueError
|
||||
assert exception_text.find("ZeroDivisionError") < exception_text.find("ValueError")
|
||||
|
||||
|
||||
def test_caused_exception():
|
||||
console = Console(width=100, file=io.StringIO())
|
||||
value_error_message = "ValueError caused by ZeroDivisionError"
|
||||
|
||||
try:
|
||||
try:
|
||||
1 / 0
|
||||
except ZeroDivisionError as e:
|
||||
raise ValueError(value_error_message) from e
|
||||
except Exception:
|
||||
console.print_exception()
|
||||
exception_text = console.file.getvalue()
|
||||
|
||||
text_should_contain = [
|
||||
value_error_message,
|
||||
"ZeroDivisionError",
|
||||
"ValueError",
|
||||
"The above exception was the direct cause",
|
||||
]
|
||||
|
||||
for msg in text_should_contain:
|
||||
assert msg in exception_text
|
||||
|
||||
# ZeroDivisionError should come before ValueError
|
||||
assert exception_text.find("ZeroDivisionError") < exception_text.find("ValueError")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue