[BUG] Rich enters infinite loop in certain cases when printing exceptions
- [x] I've checked docs and closed issues for possible solutions.
- [x] I can't find my issue in the FAQ.
Describe the bug
console.print_exception will enter an infinite loop when exc.__cause__ is exc. This can happen if we raise exc from exc. Here is the minimal example. Python enters infinite loop eating all resources until killed.
from rich.console import Console
console = Console()
def foo() -> None:
try:
raise RuntimeError("Hello")
except Exception as e:
raise e from e
def bar():
try:
foo()
except Exception as e:
assert e is e.__cause__
console.print_exception(show_locals=True)
bar()
I suspect the culprit is here:
https://github.com/Textualize/rich/blob/0c6c75644f80530de219dae3e94f0aeb999f9b4c/rich/traceback.py#L564-L572
I image the patch to look smth like this, but I am not familiar with the codebase. I am not sure if the same can happen with the __context__ attribute a few lines below the snippet above.
cause = getattr(exc_value, "__cause__", None)
- if cause:
+ if cause and cause is not exc_value:
exc_type = cause.__class__
exc_value = cause
# __traceback__ can be None, e.g. for exceptions raised by the
# 'multiprocessing' module
traceback = cause.__traceback__
is_cause = True
continue
Now I know that raise exc from exc does not make sense and should just be raise exc. But nevertheless, Rich should handle this case as well and not loop forever until it eats the whole RAM. Some python libraries unfortunately use raise exc from exc and fixing all of them is not a viable option.
I am using the latest release of Rich with Python 3.13.3
Thank you for your issue. Give us a little time to review it.
PS. You might want to check the FAQ if you haven't done so already.
This is an automated reply, generated by FAQtory
Out of interest, why would you use raise e from e ?
I wouldn't, but a library I use does. I created a MR to fix that in said library, but I still think it is worthwhile fixing here too. raise e from e is syntactically correct after all.
I wasn't proposing not fixing it.