From 085122a999920ecd6e925abfdeff640fba0fa836 Mon Sep 17 00:00:00 2001 From: Delgan Date: Wed, 6 Sep 2023 21:03:25 +0200 Subject: [PATCH] wip --- loguru/_better_exceptions.py | 80 ++++- .../output/latest/grouped_max_depth.txt | 323 ++++++++++++++++++ .../output/latest/grouped_max_length.txt | 85 +++++ .../output/latest/grouped_nested.txt | 150 ++++++++ .../output/latest/grouped_simple.txt | 100 ++++++ .../latest/grouped_with_cause_and_context.txt | 136 ++++++++ .../source/latest/grouped_max_depth.py | 26 ++ .../source/latest/grouped_max_length.py | 15 + .../source/latest/grouped_nested.py | 40 +++ .../source/latest/grouped_simple.py | 44 +++ .../latest/grouped_with_cause_and_context.py | 46 +++ tests/test_exceptions_formatting.py | 17 + 12 files changed, 1052 insertions(+), 10 deletions(-) create mode 100644 tests/exceptions/output/latest/grouped_max_depth.txt create mode 100644 tests/exceptions/output/latest/grouped_max_length.txt create mode 100644 tests/exceptions/output/latest/grouped_nested.txt create mode 100644 tests/exceptions/output/latest/grouped_simple.txt create mode 100644 tests/exceptions/output/latest/grouped_with_cause_and_context.txt create mode 100644 tests/exceptions/source/latest/grouped_max_depth.py create mode 100644 tests/exceptions/source/latest/grouped_max_length.py create mode 100644 tests/exceptions/source/latest/grouped_nested.py create mode 100644 tests/exceptions/source/latest/grouped_simple.py create mode 100644 tests/exceptions/source/latest/grouped_with_cause_and_context.py diff --git a/loguru/_better_exceptions.py b/loguru/_better_exceptions.py index 5b3de904..4c6a853e 100644 --- a/loguru/_better_exceptions.py +++ b/loguru/_better_exceptions.py @@ -10,6 +10,16 @@ import tokenize import traceback +if sys.version_info >= (3, 11): + + def is_exception_group(exc): + return isinstance(exc, ExceptionGroup) + +else: + + def is_exception_group(exc): + return False + class SyntaxHighlighter: _default_style = { @@ -344,7 +354,9 @@ def _format_locations(self, frames_lines, *, has_introduction): yield frame - def _format_exception(self, value, tb, *, seen=None, is_first=False, from_decorator=False): + def _format_exception( + self, value, tb, *, seen=None, is_first=False, from_decorator=False, group_nesting=0 + ): # Implemented from built-in traceback module: # https://github.com/python/cpython/blob/a5b76167/Lib/traceback.py#L468 exc_type, exc_value, exc_traceback = type(value), value, tb @@ -354,19 +366,30 @@ def _format_exception(self, value, tb, *, seen=None, is_first=False, from_decora seen.add(id(exc_value)) + def indent(text): + if group_nesting == 0: + yield text + return + for line in text.splitlines(True): + indented = " " * group_nesting + "| " + line + yield indented.rstrip() + "\n" + if exc_value: if exc_value.__cause__ is not None and id(exc_value.__cause__) not in seen: for text in self._format_exception( - exc_value.__cause__, exc_value.__cause__.__traceback__, seen=seen + exc_value.__cause__, + exc_value.__cause__.__traceback__, + seen=seen, + group_nesting=group_nesting, ): yield text cause = "The above exception was the direct cause of the following exception:" if self._colorize: cause = self._theme["cause"].format(cause) if self._diagnose: - yield "\n\n" + cause + "\n\n\n" + yield from indent("\n\n" + cause + "\n\n\n") else: - yield "\n" + cause + "\n\n" + yield from indent("\n" + cause + "\n\n") elif ( exc_value.__context__ is not None @@ -374,16 +397,19 @@ def _format_exception(self, value, tb, *, seen=None, is_first=False, from_decora and not exc_value.__suppress_context__ ): for text in self._format_exception( - exc_value.__context__, exc_value.__context__.__traceback__, seen=seen + exc_value.__context__, + exc_value.__context__.__traceback__, + seen=seen, + group_nesting=group_nesting, ): yield text context = "During handling of the above exception, another exception occurred:" if self._colorize: context = self._theme["context"].format(context) if self._diagnose: - yield "\n\n" + context + "\n\n\n" + yield from indent("\n\n" + context + "\n\n\n") else: - yield "\n" + context + "\n\n" + yield from indent("\n" + context + "\n\n") try: tracebacklimit = sys.tracebacklimit @@ -426,12 +452,46 @@ def _format_exception(self, value, tb, *, seen=None, is_first=False, from_decora yield self._prefix if has_introduction: - introduction = "Traceback (most recent call last):" + if is_exception_group(value): + introduction = "Exception Group Traceback (most recent call last):" + else: + introduction = "Traceback (most recent call last):" if self._colorize: introduction = self._theme["introduction"].format(introduction) - yield introduction + "\n" + if is_exception_group(value) and group_nesting == 0: + yield " + " + introduction + "\n" + else: + yield from indent(introduction + "\n") + + if is_exception_group(value) and group_nesting == 0: + group_nesting = 1 - yield "".join(frames_lines) + if group_nesting <= 10 or not is_exception_group(value): + yield from indent("".join(frames_lines)) + + if is_exception_group(value): + if group_nesting > 10: + yield " " * (group_nesting) + "| ... (max_group_depth is 10)\n" + else: + for n, exc in enumerate(value.exceptions, start=1): + pref = "+-+" if n == 1 else " +" + if n > 15: + yield " " * (group_nesting) + pref + " ... ".center(35, "-") + "\n" + yield " " * (group_nesting) + " | and %d more exceptions\n" % ( + len(value.exceptions) - 15 + ) + break + yield " " * (group_nesting) + pref + (" %d " % n).center(35, "-") + "\n" + lines = self._format_exception( + exc, + exc.__traceback__, + seen=seen, + group_nesting=group_nesting + 1, + ) + yield "".join(lines) + + if not is_exception_group(exc) or group_nesting == 10: + yield " " * (group_nesting + 1) + "+------------------------------------\n" def format_exception(self, type_, value, tb, *, from_decorator=False): yield from self._format_exception(value, tb, is_first=True, from_decorator=from_decorator) diff --git a/tests/exceptions/output/latest/grouped_max_depth.txt b/tests/exceptions/output/latest/grouped_max_depth.txt new file mode 100644 index 00000000..b4ecf876 --- /dev/null +++ b/tests/exceptions/output/latest/grouped_max_depth.txt @@ -0,0 +1,323 @@ + + + Exception Group Traceback (most recent call last): + | File "tests/exceptions/source/latest/grouped_max_depth.py", line 26, in + | main() + | File "tests/exceptions/source/latest/grouped_max_depth.py", line 19, in main + | raise ExceptionGroup("group", [nesting_left, nesting_right, nesting_both]) + | ExceptionGroup: group (3 sub-exceptions) + +-+---------------- 1 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -99 + +---------------- 2 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -98 + +---------------- 2 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -97 + +---------------- 2 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -96 + +---------------- 2 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -95 + +---------------- 2 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -94 + +---------------- 2 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -93 + +---------------- 2 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -92 + +---------------- 2 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -91 + +---------------- 2 ---------------- + | ... (max_group_depth is 10) + +------------------------------------ + +---------------- 2 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ... (max_group_depth is 10) + +---------------- 2 ---------------- + | ValueError: 91 + +------------------------------------ + +---------------- 2 ---------------- + | ValueError: 92 + +------------------------------------ + +---------------- 2 ---------------- + | ValueError: 93 + +------------------------------------ + +---------------- 2 ---------------- + | ValueError: 94 + +------------------------------------ + +---------------- 2 ---------------- + | ValueError: 95 + +------------------------------------ + +---------------- 2 ---------------- + | ValueError: 96 + +------------------------------------ + +---------------- 2 ---------------- + | ValueError: 97 + +------------------------------------ + +---------------- 2 ---------------- + | ValueError: 98 + +------------------------------------ + +---------------- 2 ---------------- + | ValueError: 99 + +------------------------------------ + +---------------- 3 ---------------- + | ExceptionGroup: group (3 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -99 + +---------------- 2 ---------------- + | ExceptionGroup: group (3 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -98 + +---------------- 2 ---------------- + | ExceptionGroup: group (3 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -97 + +---------------- 2 ---------------- + | ExceptionGroup: group (3 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -96 + +---------------- 2 ---------------- + | ExceptionGroup: group (3 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -95 + +---------------- 2 ---------------- + | ExceptionGroup: group (3 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -94 + +---------------- 2 ---------------- + | ExceptionGroup: group (3 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -93 + +---------------- 2 ---------------- + | ExceptionGroup: group (3 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -92 + +---------------- 2 ---------------- + | ExceptionGroup: group (3 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -91 + +---------------- 2 ---------------- + | ... (max_group_depth is 10) + +---------------- 3 ---------------- + | ValueError: 91 + +------------------------------------ + +---------------- 3 ---------------- + | ValueError: 92 + +------------------------------------ + +---------------- 3 ---------------- + | ValueError: 93 + +------------------------------------ + +---------------- 3 ---------------- + | ValueError: 94 + +------------------------------------ + +---------------- 3 ---------------- + | ValueError: 95 + +------------------------------------ + +---------------- 3 ---------------- + | ValueError: 96 + +------------------------------------ + +---------------- 3 ---------------- + | ValueError: 97 + +------------------------------------ + +---------------- 3 ---------------- + | ValueError: 98 + +------------------------------------ + +---------------- 3 ---------------- + | ValueError: 99 + +------------------------------------ + + + Exception Group Traceback (most recent call last): + | + | > File "tests/exceptions/source/latest/grouped_max_depth.py", line 26, in  + | main() + | └  + | + | File "tests/exceptions/source/latest/grouped_max_depth.py", line 19, in main + | raise ExceptionGroup("group", [nesting_left, nesting_right, nesting_both]) + |  │ │ └ ExceptionGroup('group', [ValueError(-99), ExceptionGroup('group', [ValueError(-98), ExceptionGroup('group', [ValueError(-97),... + |  │ └ ExceptionGroup('group', [ExceptionGroup('group', [ExceptionGroup('group', [ExceptionGroup('group', [ExceptionGroup('group', [... + |  └ ExceptionGroup('group', [ValueError(-99), ExceptionGroup('group', [ValueError(-98), ExceptionGroup('group', [ValueError(-97),... + | + | ExceptionGroup: group (3 sub-exceptions) + +-+---------------- 1 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -99 + +---------------- 2 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -98 + +---------------- 2 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -97 + +---------------- 2 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -96 + +---------------- 2 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -95 + +---------------- 2 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -94 + +---------------- 2 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -93 + +---------------- 2 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -92 + +---------------- 2 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -91 + +---------------- 2 ---------------- + | ... (max_group_depth is 10) + +------------------------------------ + +---------------- 2 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ExceptionGroup: group (2 sub-exceptions) + +-+---------------- 1 ---------------- + | ... (max_group_depth is 10) + +---------------- 2 ---------------- + | ValueError: 91 + +------------------------------------ + +---------------- 2 ---------------- + | ValueError: 92 + +------------------------------------ + +---------------- 2 ---------------- + | ValueError: 93 + +------------------------------------ + +---------------- 2 ---------------- + | ValueError: 94 + +------------------------------------ + +---------------- 2 ---------------- + | ValueError: 95 + +------------------------------------ + +---------------- 2 ---------------- + | ValueError: 96 + +------------------------------------ + +---------------- 2 ---------------- + | ValueError: 97 + +------------------------------------ + +---------------- 2 ---------------- + | ValueError: 98 + +------------------------------------ + +---------------- 2 ---------------- + | ValueError: 99 + +------------------------------------ + +---------------- 3 ---------------- + | ExceptionGroup: group (3 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -99 + +---------------- 2 ---------------- + | ExceptionGroup: group (3 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -98 + +---------------- 2 ---------------- + | ExceptionGroup: group (3 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -97 + +---------------- 2 ---------------- + | ExceptionGroup: group (3 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -96 + +---------------- 2 ---------------- + | ExceptionGroup: group (3 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -95 + +---------------- 2 ---------------- + | ExceptionGroup: group (3 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -94 + +---------------- 2 ---------------- + | ExceptionGroup: group (3 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -93 + +---------------- 2 ---------------- + | ExceptionGroup: group (3 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -92 + +---------------- 2 ---------------- + | ExceptionGroup: group (3 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: -91 + +---------------- 2 ---------------- + | ... (max_group_depth is 10) + +---------------- 3 ---------------- + | ValueError: 91 + +------------------------------------ + +---------------- 3 ---------------- + | ValueError: 92 + +------------------------------------ + +---------------- 3 ---------------- + | ValueError: 93 + +------------------------------------ + +---------------- 3 ---------------- + | ValueError: 94 + +------------------------------------ + +---------------- 3 ---------------- + | ValueError: 95 + +------------------------------------ + +---------------- 3 ---------------- + | ValueError: 96 + +------------------------------------ + +---------------- 3 ---------------- + | ValueError: 97 + +------------------------------------ + +---------------- 3 ---------------- + | ValueError: 98 + +------------------------------------ + +---------------- 3 ---------------- + | ValueError: 99 + +------------------------------------ diff --git a/tests/exceptions/output/latest/grouped_max_length.txt b/tests/exceptions/output/latest/grouped_max_length.txt new file mode 100644 index 00000000..cdd36f2b --- /dev/null +++ b/tests/exceptions/output/latest/grouped_max_length.txt @@ -0,0 +1,85 @@ + + + Exception Group Traceback (most recent call last): + | File "tests/exceptions/source/latest/grouped_max_length.py", line 15, in + | main() + | File "tests/exceptions/source/latest/grouped_max_length.py", line 8, in main + | raise ExceptionGroup("group", errors) + | ExceptionGroup: group (100 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: 0 + +---------------- 2 ---------------- + | ValueError: 1 + +---------------- 3 ---------------- + | ValueError: 2 + +---------------- 4 ---------------- + | ValueError: 3 + +---------------- 5 ---------------- + | ValueError: 4 + +---------------- 6 ---------------- + | ValueError: 5 + +---------------- 7 ---------------- + | ValueError: 6 + +---------------- 8 ---------------- + | ValueError: 7 + +---------------- 9 ---------------- + | ValueError: 8 + +---------------- 10 --------------- + | ValueError: 9 + +---------------- 11 --------------- + | ValueError: 10 + +---------------- 12 --------------- + | ValueError: 11 + +---------------- 13 --------------- + | ValueError: 12 + +---------------- 14 --------------- + | ValueError: 13 + +---------------- 15 --------------- + | ValueError: 14 + +--------------- ... --------------- + | and 85 more exceptions + +------------------------------------ + + + Exception Group Traceback (most recent call last): + | + | > File "tests/exceptions/source/latest/grouped_max_length.py", line 15, in  + | main() + | └  + | + | File "tests/exceptions/source/latest/grouped_max_length.py", line 8, in main + | raise ExceptionGroup("group", errors) + |  └ [ValueError(0), ValueError(1), ValueError(2), ValueError(3), ValueError(4), ValueError(5), ValueError(6), ValueError(7), Valu... + | + | ExceptionGroup: group (100 sub-exceptions) + +-+---------------- 1 ---------------- + | ValueError: 0 + +---------------- 2 ---------------- + | ValueError: 1 + +---------------- 3 ---------------- + | ValueError: 2 + +---------------- 4 ---------------- + | ValueError: 3 + +---------------- 5 ---------------- + | ValueError: 4 + +---------------- 6 ---------------- + | ValueError: 5 + +---------------- 7 ---------------- + | ValueError: 6 + +---------------- 8 ---------------- + | ValueError: 7 + +---------------- 9 ---------------- + | ValueError: 8 + +---------------- 10 --------------- + | ValueError: 9 + +---------------- 11 --------------- + | ValueError: 10 + +---------------- 12 --------------- + | ValueError: 11 + +---------------- 13 --------------- + | ValueError: 12 + +---------------- 14 --------------- + | ValueError: 13 + +---------------- 15 --------------- + | ValueError: 14 + +--------------- ... --------------- + | and 85 more exceptions + +------------------------------------ diff --git a/tests/exceptions/output/latest/grouped_nested.txt b/tests/exceptions/output/latest/grouped_nested.txt new file mode 100644 index 00000000..94c0d5c4 --- /dev/null +++ b/tests/exceptions/output/latest/grouped_nested.txt @@ -0,0 +1,150 @@ + + + Exception Group Traceback (most recent call last): + | File "tests/exceptions/source/latest/grouped_nested.py", line 40, in + | main() + | File "tests/exceptions/source/latest/grouped_nested.py", line 33, in main + | raise ExceptionGroup("group_2", [error_4, error_3]) from None + | ExceptionGroup: group_2 (2 sub-exceptions) + +-+---------------- 1 ---------------- + | Exception Group Traceback (most recent call last): + | File "tests/exceptions/source/latest/grouped_nested.py", line 26, in main + | raise ExceptionGroup("group_1", [error_1, error_2]) + | ExceptionGroup: group_1 (2 sub-exceptions) + +-+---------------- 1 ---------------- + | Traceback (most recent call last): + | File "tests/exceptions/source/latest/grouped_nested.py", line 17, in main + | divide_by_zero() + | File "tests/exceptions/source/latest/grouped_nested.py", line 6, in divide_by_zero + | 1 / 0 + | ZeroDivisionError: division by zero + +---------------- 2 ---------------- + | Traceback (most recent call last): + | File "tests/exceptions/source/latest/grouped_nested.py", line 22, in main + | raise_value_error(100) + | File "tests/exceptions/source/latest/grouped_nested.py", line 10, in raise_value_error + | raise ValueError(value) + | ValueError: 100 + +------------------------------------ + | + | During handling of the above exception, another exception occurred: + | + | Traceback (most recent call last): + | File "tests/exceptions/source/latest/grouped_nested.py", line 29, in main + | raise_value_error(-100) + | File "tests/exceptions/source/latest/grouped_nested.py", line 10, in raise_value_error + | raise ValueError(value) + | ValueError: -100 + +---------------- 2 ---------------- + | Exception Group Traceback (most recent call last): + | File "tests/exceptions/source/latest/grouped_nested.py", line 26, in main + | raise ExceptionGroup("group_1", [error_1, error_2]) + | ExceptionGroup: group_1 (2 sub-exceptions) + +-+---------------- 1 ---------------- + | Traceback (most recent call last): + | File "tests/exceptions/source/latest/grouped_nested.py", line 17, in main + | divide_by_zero() + | File "tests/exceptions/source/latest/grouped_nested.py", line 6, in divide_by_zero + | 1 / 0 + | ZeroDivisionError: division by zero + +---------------- 2 ---------------- + | Traceback (most recent call last): + | File "tests/exceptions/source/latest/grouped_nested.py", line 22, in main + | raise_value_error(100) + | File "tests/exceptions/source/latest/grouped_nested.py", line 10, in raise_value_error + | raise ValueError(value) + | ValueError: 100 + +------------------------------------ + + + Exception Group Traceback (most recent call last): + | + | > File "tests/exceptions/source/latest/grouped_nested.py", line 40, in  + | main() + | └  + | + | File "tests/exceptions/source/latest/grouped_nested.py", line 33, in main + | raise ExceptionGroup("group_2", [error_4, error_3]) from None + |  └ ValueError(-100) + | + | ExceptionGroup: group_2 (2 sub-exceptions) + +-+---------------- 1 ---------------- + | Exception Group Traceback (most recent call last): + | + | File "tests/exceptions/source/latest/grouped_nested.py", line 26, in main + | raise ExceptionGroup("group_1", [error_1, error_2]) + |  │ └ ValueError(100) + |  └ ZeroDivisionError('division by zero') + | + | ExceptionGroup: group_1 (2 sub-exceptions) + +-+---------------- 1 ---------------- + | Traceback (most recent call last): + | + | File "tests/exceptions/source/latest/grouped_nested.py", line 17, in main + | divide_by_zero() + | └  + | + | File "tests/exceptions/source/latest/grouped_nested.py", line 6, in divide_by_zero + | 1 / 0 + | + | ZeroDivisionError: division by zero + +---------------- 2 ---------------- + | Traceback (most recent call last): + | + | File "tests/exceptions/source/latest/grouped_nested.py", line 22, in main + | raise_value_error(100) + | └  + | + | File "tests/exceptions/source/latest/grouped_nested.py", line 10, in raise_value_error + | raise ValueError(value) + |  └ 100 + | + | ValueError: 100 + +------------------------------------ + | + | + | During handling of the above exception, another exception occurred: + | + | + | Traceback (most recent call last): + | + | File "tests/exceptions/source/latest/grouped_nested.py", line 29, in main + | raise_value_error(-100) + | └  + | + | File "tests/exceptions/source/latest/grouped_nested.py", line 10, in raise_value_error + | raise ValueError(value) + |  └ -100 + | + | ValueError: -100 + +---------------- 2 ---------------- + | Exception Group Traceback (most recent call last): + | + | File "tests/exceptions/source/latest/grouped_nested.py", line 26, in main + | raise ExceptionGroup("group_1", [error_1, error_2]) + |  │ └ ValueError(100) + |  └ ZeroDivisionError('division by zero') + | + | ExceptionGroup: group_1 (2 sub-exceptions) + +-+---------------- 1 ---------------- + | Traceback (most recent call last): + | + | File "tests/exceptions/source/latest/grouped_nested.py", line 17, in main + | divide_by_zero() + | └  + | + | File "tests/exceptions/source/latest/grouped_nested.py", line 6, in divide_by_zero + | 1 / 0 + | + | ZeroDivisionError: division by zero + +---------------- 2 ---------------- + | Traceback (most recent call last): + | + | File "tests/exceptions/source/latest/grouped_nested.py", line 22, in main + | raise_value_error(100) + | └  + | + | File "tests/exceptions/source/latest/grouped_nested.py", line 10, in raise_value_error + | raise ValueError(value) + |  └ 100 + | + | ValueError: 100 + +------------------------------------ diff --git a/tests/exceptions/output/latest/grouped_simple.txt b/tests/exceptions/output/latest/grouped_simple.txt new file mode 100644 index 00000000..959d3b9d --- /dev/null +++ b/tests/exceptions/output/latest/grouped_simple.txt @@ -0,0 +1,100 @@ + + + Exception Group Traceback (most recent call last): + | File "tests/exceptions/source/latest/grouped_simple.py", line 44, in + | main() + | File "tests/exceptions/source/latest/grouped_simple.py", line 37, in main + | raise ExceptionGroup("group", [error_1, error_2, error_3]) from None + | ExceptionGroup: group (3 sub-exceptions) + +-+---------------- 1 ---------------- + | Traceback (most recent call last): + | File "tests/exceptions/source/latest/grouped_simple.py", line 29, in main + | c(b) + | File "tests/exceptions/source/latest/grouped_simple.py", line 23, in c + | f() + | File "tests/exceptions/source/latest/grouped_simple.py", line 19, in b + | a() + | File "tests/exceptions/source/latest/grouped_simple.py", line 15, in a + | x / y + | ZeroDivisionError: division by zero + +---------------- 2 ---------------- + | Traceback (most recent call last): + | File "tests/exceptions/source/latest/grouped_simple.py", line 32, in main + | c(a) + | File "tests/exceptions/source/latest/grouped_simple.py", line 23, in c + | f() + | File "tests/exceptions/source/latest/grouped_simple.py", line 15, in a + | x / y + | ZeroDivisionError: division by zero + +---------------- 3 ---------------- + | Traceback (most recent call last): + | File "tests/exceptions/source/latest/grouped_simple.py", line 35, in main + | a() + | File "tests/exceptions/source/latest/grouped_simple.py", line 15, in a + | x / y + | ZeroDivisionError: division by zero + +------------------------------------ + + + Exception Group Traceback (most recent call last): + | + | > File "tests/exceptions/source/latest/grouped_simple.py", line 44, in  + | main() + | └  + | + | File "tests/exceptions/source/latest/grouped_simple.py", line 37, in main + | raise ExceptionGroup("group", [error_1, error_2, error_3]) from None + | + | ExceptionGroup: group (3 sub-exceptions) + +-+---------------- 1 ---------------- + | Traceback (most recent call last): + | + | File "tests/exceptions/source/latest/grouped_simple.py", line 29, in main + | c(b) + | │ └  + | └  + | + | File "tests/exceptions/source/latest/grouped_simple.py", line 23, in c + | f() + | └  + | + | File "tests/exceptions/source/latest/grouped_simple.py", line 19, in b + | a() + | └  + | + | File "tests/exceptions/source/latest/grouped_simple.py", line 15, in a + | x / y + | │ └ 0 + | └ 1 + | + | ZeroDivisionError: division by zero + +---------------- 2 ---------------- + | Traceback (most recent call last): + | + | File "tests/exceptions/source/latest/grouped_simple.py", line 32, in main + | c(a) + | │ └  + | └  + | + | File "tests/exceptions/source/latest/grouped_simple.py", line 23, in c + | f() + | └  + | + | File "tests/exceptions/source/latest/grouped_simple.py", line 15, in a + | x / y + | │ └ 0 + | └ 1 + | + | ZeroDivisionError: division by zero + +---------------- 3 ---------------- + | Traceback (most recent call last): + | + | File "tests/exceptions/source/latest/grouped_simple.py", line 35, in main + | a() + | └  + | + | File "tests/exceptions/source/latest/grouped_simple.py", line 15, in a + | x / y + | │ └ 0 + | └ 1 + | + | ZeroDivisionError: division by zero + +------------------------------------ diff --git a/tests/exceptions/output/latest/grouped_with_cause_and_context.txt b/tests/exceptions/output/latest/grouped_with_cause_and_context.txt new file mode 100644 index 00000000..a9d8fc33 --- /dev/null +++ b/tests/exceptions/output/latest/grouped_with_cause_and_context.txt @@ -0,0 +1,136 @@ + +Traceback (most recent call last): + File "tests/exceptions/source/latest/grouped_with_cause_and_context.py", line 34, in main + a() + File "tests/exceptions/source/latest/grouped_with_cause_and_context.py", line 13, in a + 1 / 0 +ZeroDivisionError: division by zero + +The above exception was the direct cause of the following exception: + +Traceback (most recent call last): + File "tests/exceptions/source/latest/grouped_with_cause_and_context.py", line 37, in main + raise ValueError("Error") from err +ValueError: Error + +During handling of the above exception, another exception occurred: + + + Exception Group Traceback (most recent call last): + | File "tests/exceptions/source/latest/grouped_with_cause_and_context.py", line 46, in + | main() + | File "tests/exceptions/source/latest/grouped_with_cause_and_context.py", line 39, in main + | raise ExceptionGroup("from_context", [from_context, from_cause]) + | ExceptionGroup: from_context (2 sub-exceptions) + +-+---------------- 1 ---------------- + | Traceback (most recent call last): + | File "tests/exceptions/source/latest/grouped_with_cause_and_context.py", line 20, in main + | a() + | File "tests/exceptions/source/latest/grouped_with_cause_and_context.py", line 13, in a + | 1 / 0 + | ZeroDivisionError: division by zero + | + | The above exception was the direct cause of the following exception: + | + | Traceback (most recent call last): + | File "tests/exceptions/source/latest/grouped_with_cause_and_context.py", line 22, in main + | raise ValueError("ContextError") from err + | ValueError: ContextError + +---------------- 2 ---------------- + | Traceback (most recent call last): + | File "tests/exceptions/source/latest/grouped_with_cause_and_context.py", line 27, in main + | a() + | File "tests/exceptions/source/latest/grouped_with_cause_and_context.py", line 13, in a + | 1 / 0 + | ZeroDivisionError: division by zero + | + | During handling of the above exception, another exception occurred: + | + | Traceback (most recent call last): + | File "tests/exceptions/source/latest/grouped_with_cause_and_context.py", line 29, in main + | raise ValueError("CauseError") + | ValueError: CauseError + +------------------------------------ + +Traceback (most recent call last): + + File "tests/exceptions/source/latest/grouped_with_cause_and_context.py", line 34, in main + a() + └  + + File "tests/exceptions/source/latest/grouped_with_cause_and_context.py", line 13, in a + 1 / 0 + +ZeroDivisionError: division by zero + + +The above exception was the direct cause of the following exception: + + +Traceback (most recent call last): + + File "tests/exceptions/source/latest/grouped_with_cause_and_context.py", line 37, in main + raise ValueError("Error") from err + +ValueError: Error + + +During handling of the above exception, another exception occurred: + + + + Exception Group Traceback (most recent call last): + | + | > File "tests/exceptions/source/latest/grouped_with_cause_and_context.py", line 46, in  + | main() + | └  + | + | File "tests/exceptions/source/latest/grouped_with_cause_and_context.py", line 39, in main + | raise ExceptionGroup("from_context", [from_context, from_cause]) + |  │ └ ValueError('CauseError') + |  └ ValueError('ContextError') + | + | ExceptionGroup: from_context (2 sub-exceptions) + +-+---------------- 1 ---------------- + | Traceback (most recent call last): + | + | File "tests/exceptions/source/latest/grouped_with_cause_and_context.py", line 20, in main + | a() + | └  + | + | File "tests/exceptions/source/latest/grouped_with_cause_and_context.py", line 13, in a + | 1 / 0 + | + | ZeroDivisionError: division by zero + | + | + | The above exception was the direct cause of the following exception: + | + | + | Traceback (most recent call last): + | + | File "tests/exceptions/source/latest/grouped_with_cause_and_context.py", line 22, in main + | raise ValueError("ContextError") from err + | + | ValueError: ContextError + +---------------- 2 ---------------- + | Traceback (most recent call last): + | + | File "tests/exceptions/source/latest/grouped_with_cause_and_context.py", line 27, in main + | a() + | └  + | + | File "tests/exceptions/source/latest/grouped_with_cause_and_context.py", line 13, in a + | 1 / 0 + | + | ZeroDivisionError: division by zero + | + | + | During handling of the above exception, another exception occurred: + | + | + | Traceback (most recent call last): + | + | File "tests/exceptions/source/latest/grouped_with_cause_and_context.py", line 29, in main + | raise ValueError("CauseError") + | + | ValueError: CauseError + +------------------------------------ diff --git a/tests/exceptions/source/latest/grouped_max_depth.py b/tests/exceptions/source/latest/grouped_max_depth.py new file mode 100644 index 00000000..1eebefe6 --- /dev/null +++ b/tests/exceptions/source/latest/grouped_max_depth.py @@ -0,0 +1,26 @@ +from loguru import logger +import sys + + +@logger.catch +def main(): + nesting_left = ValueError("Left") + for i in range(100): + nesting_left = ExceptionGroup("group", [ValueError(-i), nesting_left]) + + nesting_right = ValueError("Right") + for i in range(100): + nesting_right = ExceptionGroup("group", [nesting_right, ValueError(i)]) + + nesting_both = ValueError("Both") + for i in range(100): + nesting_both = ExceptionGroup("group", [ValueError(-i), nesting_both, ValueError(i)]) + + raise ExceptionGroup("group", [nesting_left, nesting_right, nesting_both]) + + +logger.remove() +logger.add(sys.stderr, format="", diagnose=False, backtrace=False, colorize=False) +logger.add(sys.stderr, format="", diagnose=True, backtrace=True, colorize=True) + +main() diff --git a/tests/exceptions/source/latest/grouped_max_length.py b/tests/exceptions/source/latest/grouped_max_length.py new file mode 100644 index 00000000..5346668e --- /dev/null +++ b/tests/exceptions/source/latest/grouped_max_length.py @@ -0,0 +1,15 @@ +from loguru import logger +import sys + + +@logger.catch +def main(): + errors = [ValueError(i) for i in range(100)] + raise ExceptionGroup("group", errors) + + +logger.remove() +logger.add(sys.stderr, format="", diagnose=False, backtrace=False, colorize=False) +logger.add(sys.stderr, format="", diagnose=True, backtrace=True, colorize=True) + +main() diff --git a/tests/exceptions/source/latest/grouped_nested.py b/tests/exceptions/source/latest/grouped_nested.py new file mode 100644 index 00000000..fb58f3ce --- /dev/null +++ b/tests/exceptions/source/latest/grouped_nested.py @@ -0,0 +1,40 @@ +from loguru import logger +import sys + + +def divide_by_zero(): + 1 / 0 + + +def raise_value_error(value): + raise ValueError(value) + + +@logger.catch +def main(): + try: + try: + divide_by_zero() + except Exception as err: + error_1 = err + + try: + raise_value_error(100) + except Exception as err: + error_2 = err + + raise ExceptionGroup("group_1", [error_1, error_2]) + except ExceptionGroup as error_3: + try: + raise_value_error(-100) + except Exception as err: + error_4 = err + + raise ExceptionGroup("group_2", [error_4, error_3]) from None + + +logger.remove() +logger.add(sys.stderr, format="", diagnose=False, backtrace=False, colorize=False) +logger.add(sys.stderr, format="", diagnose=True, backtrace=True, colorize=True) + +main() diff --git a/tests/exceptions/source/latest/grouped_simple.py b/tests/exceptions/source/latest/grouped_simple.py new file mode 100644 index 00000000..f905f8dc --- /dev/null +++ b/tests/exceptions/source/latest/grouped_simple.py @@ -0,0 +1,44 @@ +import sys + +from loguru import logger + +logger.remove() +logger.add(sys.stderr, format="", diagnose=True, backtrace=True, colorize=False) + +from loguru import logger +import sys + + +def a(): + x = 1 + y = 0 + x / y + + +def b(): + a() + + +def c(f): + f() + + +@logger.catch +def main(): + try: + c(b) + except Exception as error_1: + try: + c(a) + except Exception as error_2: + try: + a() + except Exception as error_3: + raise ExceptionGroup("group", [error_1, error_2, error_3]) from None + + +logger.remove() +logger.add(sys.stderr, format="", diagnose=False, backtrace=False, colorize=False) +logger.add(sys.stderr, format="", diagnose=True, backtrace=True, colorize=True) + +main() diff --git a/tests/exceptions/source/latest/grouped_with_cause_and_context.py b/tests/exceptions/source/latest/grouped_with_cause_and_context.py new file mode 100644 index 00000000..b4f7ba1e --- /dev/null +++ b/tests/exceptions/source/latest/grouped_with_cause_and_context.py @@ -0,0 +1,46 @@ +import sys + +from loguru import logger + +logger.remove() +logger.add(sys.stderr, format="", diagnose=True, backtrace=True, colorize=False) + +from loguru import logger +import sys + + +def a(): + 1 / 0 + + +@logger.catch +def main(): + try: + try: + a() + except Exception as err: + raise ValueError("ContextError") from err + except Exception as err: + from_context = err + try: + try: + a() + except Exception as err: + raise ValueError("CauseError") + except Exception as err: + from_cause = err + + try: + a() + except Exception as err: + try: + raise ValueError("Error") from err + except Exception: + raise ExceptionGroup("from_context", [from_context, from_cause]) + + +logger.remove() +logger.add(sys.stderr, format="", diagnose=False, backtrace=False, colorize=False) +logger.add(sys.stderr, format="", diagnose=True, backtrace=True, colorize=True) + +main() diff --git a/tests/test_exceptions_formatting.py b/tests/test_exceptions_formatting.py index 248371b7..0c29d62e 100644 --- a/tests/test_exceptions_formatting.py +++ b/tests/test_exceptions_formatting.py @@ -224,3 +224,20 @@ def test_exception_ownership(filename): ) def test_exception_others(filename): compare_exception("others", filename) + + +@pytest.mark.parametrize( + "filename, minimum_python_version", + [ + ("grouped_simple", (3, 11)), + ("grouped_nested", (3, 11)), + ("grouped_with_cause_and_context", (3, 11)), + ("grouped_max_length", (3, 11)), + ("grouped_max_depth", (3, 11)), + ], +) +def test_exception_latest(filename, minimum_python_version): + if sys.version_info < minimum_python_version: + pytest.skip("Feature not supported in this Python version") + + compare_exception("latest", filename)