-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
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
Enable rendering of rich exception objects in traceback #3325
base: master
Are you sure you want to change the base?
Enable rendering of rich exception objects in traceback #3325
Conversation
This is currently a draft since I'm not sure how exactly exceptions within the rich object rendering should be handled -- I'm leaning toward trying to catch them and presenting a fallback like "render failed" or similar. Not quite sure how to do that, so figured I'd open this as a draft PR in case y'all have any opinions/suggestions on how to achieve that. |
OK, I think I figured out something that works! 🎉 Using the Marking this as ready for review, since I think everything that is necessary to land this is in place now1 -- code, tests, documentation, changelog, contributing.md. :) Footnotes
|
This also gracefully handles exception objects which fail to render.
9e34085
to
9cb8a34
Compare
@@ -722,7 +739,11 @@ def render_locals(frame: Frame) -> Iterable[ConsoleRenderable]: | |||
from .console import Console | |||
|
|||
console = Console() | |||
import sys |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sys was unused at this point, and there's already a top-level import sys
.
except Exception: | ||
console.print_exception() | ||
result = console.file.getvalue() | ||
print(result) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These prints are useful for diagnosing failures, and pytest will capture this output appropriately. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can see why this would be nice, but it breaks some assumptions. See comment...
@@ -416,6 +418,8 @@ def safe_str(_object: Any) -> str: | |||
line=exc_value.text or "", | |||
msg=exc_value.msg, | |||
) | |||
if isinstance(exc_value, RichRenderable): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My concern here is that if we offer arbitrary renderables, then the Traceback object may no longer be trivially sererializeable. Which was one of the goals for this class.
And since a renderable may be mutable, it may become out of date if it isn't immediately printed.
@pradyunsg Any thoughts on above? The issue with serialization may be a deal breaker. |
Thanks for the mention -- I'd missed the original review.
(I assume by serialization you mean [object] -> [rich-rendered string] and not some on-disk/pickle representation or something) I share that, which is why there's a fallback to the string-based approach in the render side of this if the serialization via the rich protocol fails. I think it's an acceptable tradeoff since this is opt-in on the exception raiser's end as this needs an additional set of methods on exception class itself. I'd be fine with adding another layer of opt-in boolean on
That's also the case with exception objects themselves and wouldn't be unique to rich's renderables ( ❯ cat /tmp/foo.py
try:
raise Exception("This is an error message")
except Exception as e:
print(e)
e.args = ("This is a new error message",)
raise
❯ python /tmp/foo.py
This is an error message
Traceback (most recent call last):
File "/tmp/foo.py", line 2, in <module>
raise Exception("This is an error message")
Exception: This is a new error message |
Type of changes
Checklist
Description
See #2717 for discussion. This enables exceptions to be rich objects and be rendered via the rich rendering pipeline, in tracebacks. This can be tested manually by running
python -m rich.traceback
whose example now uses a rich object instead of theNameError
.