How to print the __str__ representation of the entire traceback

55 Views Asked by At

When printing an exception using, for instance, print(ex), only the last exception in the chain is printed, how can I instead print all the exceptions in the chain without crowding it with excessive traceback information.

For example:

 def test_with_context(context: str, test: int)   
    try:
        assert isinstance(test, int)
        assert test > 4, "Test must be greater than 4"
        assert test < 6, "Test must be smaller than 6"
    exccept AssertionError as ex:
        raise ValueError(f"Invalid test for context {context}") from ex

try: 
    test_with_context("ExampleContext", 8)
except ValueError as ex:
    print("Value Test Failed":)
    print(ex)

Provides me with an output of

Value Test Failed
ValueError: Invalid test for context ExampleContext

Which is useful in providing with me with the overall context, but doesnt tell me what error exactly caused that ValueError.

What I would like to achieve is:

Value Test Failed
ValueError: Invalid test for context ExampleContext
AssertionError: Test must be smaller than 6

I can use:

traceback.print_exc()

But that provides me with the entire formatted traceback, line numbers and all, which is too much information to provide a user with a simple input error for instance.

---

Similarly, I have tried using

exccept AssertionError as ex:
        ex.add_note(f"Invalid test for context {context}")

But it would appear the notes dont appear at all in anything but the full context.

Is there any way to get a nice list of the exception history to print in order?

1

There are 1 best solutions below

0
Fyrefly On

I have produced a solution I'm not super keen on, but it does the job:

def cause_stack(exception: BaseException) -> List[BaseException]:
    if exception.__cause__ is None:
        return [exception]
    else:
        return [exception] + cause_stack(exception.__cause__)


def format_causes(exception: BaseException) -> str:
    return "\n - caused by -\n".join([str(cause) for cause in cause_stack(exception)])

Because the cause of each exception is stored under the .__cause__ dunder property, recursively searching that can get you a list of each cause in order, which I then format by joining them together with a "caused by" string.

Not super happy with it as a solution - doesn't feel very pythonic, uses recursion which may be problematic with larger stacks and it's not as elegant as one might hope, but it serves my needs for now.