From 01570567b7d108af43bc43003c10503eb588b22a Mon Sep 17 00:00:00 2001 From: Delgan Date: Wed, 6 Sep 2023 21:03:25 +0200 Subject: [PATCH] Add support for formatting of "ExceptionGroup" errors --- CHANGELOG.rst | 6 + loguru/_better_exceptions.py | 115 +++++-- .../modern/grouped_as_cause_and_context.txt | 186 ++++++++++ .../output/modern/grouped_max_depth.txt | 323 ++++++++++++++++++ .../output/modern/grouped_max_length.txt | 85 +++++ .../output/modern/grouped_nested.txt | 150 ++++++++ .../output/modern/grouped_simple.txt | 100 ++++++ .../modern/grouped_with_cause_and_context.txt | 136 ++++++++ .../modern/grouped_as_cause_and_context.py | 42 +++ .../source/modern/grouped_max_depth.py | 26 ++ .../source/modern/grouped_max_length.py | 15 + .../source/modern/grouped_nested.py | 40 +++ .../source/modern/grouped_simple.py | 41 +++ .../modern/grouped_with_cause_and_context.py | 43 +++ tests/test_exceptions_formatting.py | 18 + 15 files changed, 1301 insertions(+), 25 deletions(-) create mode 100644 tests/exceptions/output/modern/grouped_as_cause_and_context.txt create mode 100644 tests/exceptions/output/modern/grouped_max_depth.txt create mode 100644 tests/exceptions/output/modern/grouped_max_length.txt create mode 100644 tests/exceptions/output/modern/grouped_nested.txt create mode 100644 tests/exceptions/output/modern/grouped_simple.txt create mode 100644 tests/exceptions/output/modern/grouped_with_cause_and_context.txt create mode 100644 tests/exceptions/source/modern/grouped_as_cause_and_context.py create mode 100644 tests/exceptions/source/modern/grouped_max_depth.py create mode 100644 tests/exceptions/source/modern/grouped_max_length.py create mode 100644 tests/exceptions/source/modern/grouped_nested.py create mode 100644 tests/exceptions/source/modern/grouped_simple.py create mode 100644 tests/exceptions/source/modern/grouped_with_cause_and_context.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 34bb53f5..8d8bac98 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,9 @@ +`Unreleased`_ +============= + +- Add support for formatting of ``ExceptionGroup`` errors (`#805 `_). + + `0.7.1`_ (2023-09-04) ===================== diff --git a/loguru/_better_exceptions.py b/loguru/_better_exceptions.py index 5b3de904..481c245e 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 = { @@ -140,6 +150,15 @@ def _get_lib_dirs(): paths = {sysconfig.get_path(name, scheme) for scheme in schemes for name in names} return [os.path.abspath(path).lower() + os.sep for path in paths if path in sys.path] + @staticmethod + def _indent(text, count, *, prefix="| "): + if count == 0: + yield text + return + for line in text.splitlines(True): + indented = " " * count + prefix + line + yield indented.rstrip() + "\n" + def _get_char(self, char, default): try: char.encode(self._encoding) @@ -344,7 +363,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 @@ -356,42 +377,59 @@ def _format_exception(self, value, tb, *, seen=None, is_first=False, from_decora 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 - ): - yield text + yield from self._format_exception( + exc_value.__cause__, + exc_value.__cause__.__traceback__, + seen=seen, + group_nesting=group_nesting, + ) 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 self._indent("\n\n" + cause + "\n\n\n", group_nesting) else: - yield "\n" + cause + "\n\n" + yield from self._indent("\n" + cause + "\n\n", group_nesting) elif ( exc_value.__context__ is not None and id(exc_value.__context__) not in seen and not exc_value.__suppress_context__ ): - for text in self._format_exception( - exc_value.__context__, exc_value.__context__.__traceback__, seen=seen - ): - yield text + yield from self._format_exception( + exc_value.__context__, + exc_value.__context__.__traceback__, + seen=seen, + group_nesting=group_nesting, + ) 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 self._indent("\n\n" + context + "\n\n\n", group_nesting) else: - yield "\n" + context + "\n\n" + yield from self._indent("\n" + context + "\n\n", group_nesting) + + is_grouped = is_exception_group(value) + + if is_grouped and group_nesting == 0: + yield from self._format_exception( + value, + tb, + seen=seen, + group_nesting=1, + is_first=is_first, + from_decorator=from_decorator, + ) + return try: - tracebacklimit = sys.tracebacklimit + traceback_limit = sys.tracebacklimit except AttributeError: - tracebacklimit = None + traceback_limit = None frames, final_source = self._extract_frames( - exc_traceback, is_first, limit=tracebacklimit, from_decorator=from_decorator + exc_traceback, is_first, limit=traceback_limit, from_decorator=from_decorator ) exception_only = traceback.format_exception_only(exc_type, exc_value) @@ -416,22 +454,49 @@ def _format_exception(self, value, tb, *, seen=None, is_first=False, from_decora exception_only[-1] = error_message + "\n" - frames_lines = traceback.format_list(frames) + exception_only - has_introduction = bool(frames) - - if self._colorize or self._backtrace or self._diagnose: - frames_lines = self._format_locations(frames_lines, has_introduction=has_introduction) - if is_first: yield self._prefix + has_introduction = bool(frames) + if has_introduction: - introduction = "Traceback (most recent call last):" + if is_grouped: + 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 group_nesting == 1: # Implies we're processing the root ExceptionGroup. + yield from self._indent(introduction + "\n", group_nesting, prefix="+ ") + else: + yield from self._indent(introduction + "\n", group_nesting) + + frames_lines = traceback.format_list(frames) + exception_only + if self._colorize or self._backtrace or self._diagnose: + frames_lines = self._format_locations(frames_lines, has_introduction=has_introduction) - yield "".join(frames_lines) + yield from self._indent("".join(frames_lines), group_nesting) + + if is_grouped: + for n, exc in enumerate(value.exceptions, start=1): + ruler = "+" + (" %s " % ("..." if n > 15 else n)).center(35, "-") + yield from self._indent(ruler, group_nesting, prefix="+-" if n == 1 else " ") + if n > 15: + message = "and %d more exceptions\n" % (len(value.exceptions) - 15) + yield from self._indent(message, group_nesting + 1) + break + elif group_nesting == 10 and is_exception_group(exc): + message = "... (max_group_depth is 10)\n" + yield from self._indent(message, group_nesting + 1) + else: + yield from self._format_exception( + exc, + exc.__traceback__, + seen=seen, + group_nesting=group_nesting + 1, + ) + if not is_exception_group(exc) or group_nesting == 10: + yield from self._indent("-" * 35, group_nesting + 1, prefix="+-") 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/modern/grouped_as_cause_and_context.txt b/tests/exceptions/output/modern/grouped_as_cause_and_context.txt new file mode 100644 index 00000000..2c0e1f03 --- /dev/null +++ b/tests/exceptions/output/modern/grouped_as_cause_and_context.txt @@ -0,0 +1,186 @@ + + + Exception Group Traceback (most recent call last): + | File "tests/exceptions/source/modern/grouped_as_cause_and_context.py", line 31, 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/modern/grouped_as_cause_and_context.py", line 20, in main + | a() + | File "tests/exceptions/source/modern/grouped_as_cause_and_context.py", line 10, in a + | 1 / 0 + | ZeroDivisionError: division by zero + +---------------- 2 ---------------- + | Traceback (most recent call last): + | File "tests/exceptions/source/modern/grouped_as_cause_and_context.py", line 25, in main + | b() + | File "tests/exceptions/source/modern/grouped_as_cause_and_context.py", line 14, in b + | raise ValueError("Error") + | ValueError: Error + +------------------------------------ + +The above exception was the direct cause of the following exception: + + + Exception Group Traceback (most recent call last): + | File "tests/exceptions/source/modern/grouped_as_cause_and_context.py", line 33, in main + | raise ExceptionGroup("group_2", [error_2, error_1]) from err + | ExceptionGroup: group_2 (2 sub-exceptions) + +-+---------------- 1 ---------------- + | Traceback (most recent call last): + | File "tests/exceptions/source/modern/grouped_as_cause_and_context.py", line 25, in main + | b() + | File "tests/exceptions/source/modern/grouped_as_cause_and_context.py", line 14, in b + | raise ValueError("Error") + | ValueError: Error + +---------------- 2 ---------------- + | Traceback (most recent call last): + | File "tests/exceptions/source/modern/grouped_as_cause_and_context.py", line 20, in main + | a() + | File "tests/exceptions/source/modern/grouped_as_cause_and_context.py", line 10, in a + | 1 / 0 + | ZeroDivisionError: division by zero + +------------------------------------ + +During handling of the above exception, another exception occurred: + + + Exception Group Traceback (most recent call last): + | File "tests/exceptions/source/modern/grouped_as_cause_and_context.py", line 42, in + | main() + | File "tests/exceptions/source/modern/grouped_as_cause_and_context.py", line 35, in main + | raise ExceptionGroup("group_3", [err]) + | ExceptionGroup: group_3 (1 sub-exception) + +-+---------------- 1 ---------------- + | Exception Group Traceback (most recent call last): + | File "tests/exceptions/source/modern/grouped_as_cause_and_context.py", line 33, in main + | raise ExceptionGroup("group_2", [error_2, error_1]) from err + | ExceptionGroup: group_2 (2 sub-exceptions) + +-+---------------- 1 ---------------- + | Traceback (most recent call last): + | File "tests/exceptions/source/modern/grouped_as_cause_and_context.py", line 25, in main + | b() + | File "tests/exceptions/source/modern/grouped_as_cause_and_context.py", line 14, in b + | raise ValueError("Error") + | ValueError: Error + +---------------- 2 ---------------- + | Traceback (most recent call last): + | File "tests/exceptions/source/modern/grouped_as_cause_and_context.py", line 20, in main + | a() + | File "tests/exceptions/source/modern/grouped_as_cause_and_context.py", line 10, in a + | 1 / 0 + | ZeroDivisionError: division by zero + +------------------------------------ + + + Exception Group Traceback (most recent call last): + | + | File "tests/exceptions/source/modern/grouped_as_cause_and_context.py", line 31, in main + | raise ExceptionGroup("group_1", [error_1, error_2]) + |  │ └ ValueError('Error') + |  └ ZeroDivisionError('division by zero') + | + | ExceptionGroup: group_1 (2 sub-exceptions) + +-+---------------- 1 ---------------- + | Traceback (most recent call last): + | + | File "tests/exceptions/source/modern/grouped_as_cause_and_context.py", line 20, in main + | a() + | └  + | + | File "tests/exceptions/source/modern/grouped_as_cause_and_context.py", line 10, in a + | 1 / 0 + | + | ZeroDivisionError: division by zero + +---------------- 2 ---------------- + | Traceback (most recent call last): + | + | File "tests/exceptions/source/modern/grouped_as_cause_and_context.py", line 25, in main + | b() + | └  + | + | File "tests/exceptions/source/modern/grouped_as_cause_and_context.py", line 14, in b + | raise ValueError("Error") + | + | ValueError: Error + +------------------------------------ + + +The above exception was the direct cause of the following exception: + + + + Exception Group Traceback (most recent call last): + | + | File "tests/exceptions/source/modern/grouped_as_cause_and_context.py", line 33, in main + | raise ExceptionGroup("group_2", [error_2, error_1]) from err + |  │ └ ZeroDivisionError('division by zero') + |  └ ValueError('Error') + | + | ExceptionGroup: group_2 (2 sub-exceptions) + +-+---------------- 1 ---------------- + | Traceback (most recent call last): + | + | File "tests/exceptions/source/modern/grouped_as_cause_and_context.py", line 25, in main + | b() + | └  + | + | File "tests/exceptions/source/modern/grouped_as_cause_and_context.py", line 14, in b + | raise ValueError("Error") + | + | ValueError: Error + +---------------- 2 ---------------- + | Traceback (most recent call last): + | + | File "tests/exceptions/source/modern/grouped_as_cause_and_context.py", line 20, in main + | a() + | └  + | + | File "tests/exceptions/source/modern/grouped_as_cause_and_context.py", line 10, in a + | 1 / 0 + | + | ZeroDivisionError: division by zero + +------------------------------------ + + +During handling of the above exception, another exception occurred: + + + + Exception Group Traceback (most recent call last): + | + | > File "tests/exceptions/source/modern/grouped_as_cause_and_context.py", line 42, in  + | main() + | └  + | + | File "tests/exceptions/source/modern/grouped_as_cause_and_context.py", line 35, in main + | raise ExceptionGroup("group_3", [err]) + | + | ExceptionGroup: group_3 (1 sub-exception) + +-+---------------- 1 ---------------- + | Exception Group Traceback (most recent call last): + | + | File "tests/exceptions/source/modern/grouped_as_cause_and_context.py", line 33, in main + | raise ExceptionGroup("group_2", [error_2, error_1]) from err + |  │ └ ZeroDivisionError('division by zero') + |  └ ValueError('Error') + | + | ExceptionGroup: group_2 (2 sub-exceptions) + +-+---------------- 1 ---------------- + | Traceback (most recent call last): + | + | File "tests/exceptions/source/modern/grouped_as_cause_and_context.py", line 25, in main + | b() + | └  + | + | File "tests/exceptions/source/modern/grouped_as_cause_and_context.py", line 14, in b + | raise ValueError("Error") + | + | ValueError: Error + +---------------- 2 ---------------- + | Traceback (most recent call last): + | + | File "tests/exceptions/source/modern/grouped_as_cause_and_context.py", line 20, in main + | a() + | └  + | + | File "tests/exceptions/source/modern/grouped_as_cause_and_context.py", line 10, in a + | 1 / 0 + | + | ZeroDivisionError: division by zero + +------------------------------------ diff --git a/tests/exceptions/output/modern/grouped_max_depth.txt b/tests/exceptions/output/modern/grouped_max_depth.txt new file mode 100644 index 00000000..91efffe9 --- /dev/null +++ b/tests/exceptions/output/modern/grouped_max_depth.txt @@ -0,0 +1,323 @@ + + + Exception Group Traceback (most recent call last): + | File "tests/exceptions/source/modern/grouped_max_depth.py", line 26, in + | main() + | File "tests/exceptions/source/modern/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/modern/grouped_max_depth.py", line 26, in  + | main() + | └  + | + | File "tests/exceptions/source/modern/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/modern/grouped_max_length.txt b/tests/exceptions/output/modern/grouped_max_length.txt new file mode 100644 index 00000000..43cc4efc --- /dev/null +++ b/tests/exceptions/output/modern/grouped_max_length.txt @@ -0,0 +1,85 @@ + + + Exception Group Traceback (most recent call last): + | File "tests/exceptions/source/modern/grouped_max_length.py", line 15, in + | main() + | File "tests/exceptions/source/modern/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/modern/grouped_max_length.py", line 15, in  + | main() + | └  + | + | File "tests/exceptions/source/modern/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/modern/grouped_nested.txt b/tests/exceptions/output/modern/grouped_nested.txt new file mode 100644 index 00000000..95036aeb --- /dev/null +++ b/tests/exceptions/output/modern/grouped_nested.txt @@ -0,0 +1,150 @@ + + + Exception Group Traceback (most recent call last): + | File "tests/exceptions/source/modern/grouped_nested.py", line 40, in + | main() + | File "tests/exceptions/source/modern/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/modern/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/modern/grouped_nested.py", line 17, in main + | divide_by_zero() + | File "tests/exceptions/source/modern/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/modern/grouped_nested.py", line 22, in main + | raise_value_error(100) + | File "tests/exceptions/source/modern/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/modern/grouped_nested.py", line 29, in main + | raise_value_error(-100) + | File "tests/exceptions/source/modern/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/modern/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/modern/grouped_nested.py", line 17, in main + | divide_by_zero() + | File "tests/exceptions/source/modern/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/modern/grouped_nested.py", line 22, in main + | raise_value_error(100) + | File "tests/exceptions/source/modern/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/modern/grouped_nested.py", line 40, in  + | main() + | └  + | + | File "tests/exceptions/source/modern/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/modern/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/modern/grouped_nested.py", line 17, in main + | divide_by_zero() + | └  + | + | File "tests/exceptions/source/modern/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/modern/grouped_nested.py", line 22, in main + | raise_value_error(100) + | └  + | + | File "tests/exceptions/source/modern/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/modern/grouped_nested.py", line 29, in main + | raise_value_error(-100) + | └  + | + | File "tests/exceptions/source/modern/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/modern/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/modern/grouped_nested.py", line 17, in main + | divide_by_zero() + | └  + | + | File "tests/exceptions/source/modern/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/modern/grouped_nested.py", line 22, in main + | raise_value_error(100) + | └  + | + | File "tests/exceptions/source/modern/grouped_nested.py", line 10, in raise_value_error + | raise ValueError(value) + |  └ 100 + | + | ValueError: 100 + +------------------------------------ diff --git a/tests/exceptions/output/modern/grouped_simple.txt b/tests/exceptions/output/modern/grouped_simple.txt new file mode 100644 index 00000000..b9915cb1 --- /dev/null +++ b/tests/exceptions/output/modern/grouped_simple.txt @@ -0,0 +1,100 @@ + + + Exception Group Traceback (most recent call last): + | File "tests/exceptions/source/modern/grouped_simple.py", line 41, in + | main() + | File "tests/exceptions/source/modern/grouped_simple.py", line 34, 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/modern/grouped_simple.py", line 26, in main + | c(b) + | File "tests/exceptions/source/modern/grouped_simple.py", line 20, in c + | f() + | File "tests/exceptions/source/modern/grouped_simple.py", line 16, in b + | a() + | File "tests/exceptions/source/modern/grouped_simple.py", line 12, in a + | x / y + | ZeroDivisionError: division by zero + +---------------- 2 ---------------- + | Traceback (most recent call last): + | File "tests/exceptions/source/modern/grouped_simple.py", line 29, in main + | c(a) + | File "tests/exceptions/source/modern/grouped_simple.py", line 20, in c + | f() + | File "tests/exceptions/source/modern/grouped_simple.py", line 12, in a + | x / y + | ZeroDivisionError: division by zero + +---------------- 3 ---------------- + | Traceback (most recent call last): + | File "tests/exceptions/source/modern/grouped_simple.py", line 32, in main + | a() + | File "tests/exceptions/source/modern/grouped_simple.py", line 12, in a + | x / y + | ZeroDivisionError: division by zero + +------------------------------------ + + + Exception Group Traceback (most recent call last): + | + | > File "tests/exceptions/source/modern/grouped_simple.py", line 41, in  + | main() + | └  + | + | File "tests/exceptions/source/modern/grouped_simple.py", line 34, 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/modern/grouped_simple.py", line 26, in main + | c(b) + | │ └  + | └  + | + | File "tests/exceptions/source/modern/grouped_simple.py", line 20, in c + | f() + | └  + | + | File "tests/exceptions/source/modern/grouped_simple.py", line 16, in b + | a() + | └  + | + | File "tests/exceptions/source/modern/grouped_simple.py", line 12, in a + | x / y + | │ └ 0 + | └ 1 + | + | ZeroDivisionError: division by zero + +---------------- 2 ---------------- + | Traceback (most recent call last): + | + | File "tests/exceptions/source/modern/grouped_simple.py", line 29, in main + | c(a) + | │ └  + | └  + | + | File "tests/exceptions/source/modern/grouped_simple.py", line 20, in c + | f() + | └  + | + | File "tests/exceptions/source/modern/grouped_simple.py", line 12, in a + | x / y + | │ └ 0 + | └ 1 + | + | ZeroDivisionError: division by zero + +---------------- 3 ---------------- + | Traceback (most recent call last): + | + | File "tests/exceptions/source/modern/grouped_simple.py", line 32, in main + | a() + | └  + | + | File "tests/exceptions/source/modern/grouped_simple.py", line 12, in a + | x / y + | │ └ 0 + | └ 1 + | + | ZeroDivisionError: division by zero + +------------------------------------ diff --git a/tests/exceptions/output/modern/grouped_with_cause_and_context.txt b/tests/exceptions/output/modern/grouped_with_cause_and_context.txt new file mode 100644 index 00000000..cd5de99e --- /dev/null +++ b/tests/exceptions/output/modern/grouped_with_cause_and_context.txt @@ -0,0 +1,136 @@ + +Traceback (most recent call last): + File "tests/exceptions/source/modern/grouped_with_cause_and_context.py", line 31, in main + a() + File "tests/exceptions/source/modern/grouped_with_cause_and_context.py", line 10, 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/modern/grouped_with_cause_and_context.py", line 34, 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/modern/grouped_with_cause_and_context.py", line 43, in + | main() + | File "tests/exceptions/source/modern/grouped_with_cause_and_context.py", line 36, 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/modern/grouped_with_cause_and_context.py", line 17, in main + | a() + | File "tests/exceptions/source/modern/grouped_with_cause_and_context.py", line 10, 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/modern/grouped_with_cause_and_context.py", line 19, in main + | raise ValueError("ContextError") from err + | ValueError: ContextError + +---------------- 2 ---------------- + | Traceback (most recent call last): + | File "tests/exceptions/source/modern/grouped_with_cause_and_context.py", line 24, in main + | a() + | File "tests/exceptions/source/modern/grouped_with_cause_and_context.py", line 10, 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/modern/grouped_with_cause_and_context.py", line 26, in main + | raise ValueError("CauseError") + | ValueError: CauseError + +------------------------------------ + +Traceback (most recent call last): + + File "tests/exceptions/source/modern/grouped_with_cause_and_context.py", line 31, in main + a() + └  + + File "tests/exceptions/source/modern/grouped_with_cause_and_context.py", line 10, 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/modern/grouped_with_cause_and_context.py", line 34, 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/modern/grouped_with_cause_and_context.py", line 43, in  + | main() + | └  + | + | File "tests/exceptions/source/modern/grouped_with_cause_and_context.py", line 36, 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/modern/grouped_with_cause_and_context.py", line 17, in main + | a() + | └  + | + | File "tests/exceptions/source/modern/grouped_with_cause_and_context.py", line 10, 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/modern/grouped_with_cause_and_context.py", line 19, in main + | raise ValueError("ContextError") from err + | + | ValueError: ContextError + +---------------- 2 ---------------- + | Traceback (most recent call last): + | + | File "tests/exceptions/source/modern/grouped_with_cause_and_context.py", line 24, in main + | a() + | └  + | + | File "tests/exceptions/source/modern/grouped_with_cause_and_context.py", line 10, 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/modern/grouped_with_cause_and_context.py", line 26, in main + | raise ValueError("CauseError") + | + | ValueError: CauseError + +------------------------------------ diff --git a/tests/exceptions/source/modern/grouped_as_cause_and_context.py b/tests/exceptions/source/modern/grouped_as_cause_and_context.py new file mode 100644 index 00000000..01bdcfbd --- /dev/null +++ b/tests/exceptions/source/modern/grouped_as_cause_and_context.py @@ -0,0 +1,42 @@ +import sys + +from loguru import logger + +logger.remove() +logger.add(sys.stderr, format="", diagnose=True, backtrace=True, colorize=False) + + +def a(): + 1 / 0 + + +def b(): + raise ValueError("Error") + + +@logger.catch +def main(): + try: + a() + except Exception as err: + error_1 = err + + try: + b() + except Exception as err: + error_2 = err + + try: + try: + raise ExceptionGroup("group_1", [error_1, error_2]) + except Exception as err: + raise ExceptionGroup("group_2", [error_2, error_1]) from err + except Exception as err: + raise ExceptionGroup("group_3", [err]) + + +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/modern/grouped_max_depth.py b/tests/exceptions/source/modern/grouped_max_depth.py new file mode 100644 index 00000000..1eebefe6 --- /dev/null +++ b/tests/exceptions/source/modern/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/modern/grouped_max_length.py b/tests/exceptions/source/modern/grouped_max_length.py new file mode 100644 index 00000000..5346668e --- /dev/null +++ b/tests/exceptions/source/modern/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/modern/grouped_nested.py b/tests/exceptions/source/modern/grouped_nested.py new file mode 100644 index 00000000..fb58f3ce --- /dev/null +++ b/tests/exceptions/source/modern/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/modern/grouped_simple.py b/tests/exceptions/source/modern/grouped_simple.py new file mode 100644 index 00000000..f68c7152 --- /dev/null +++ b/tests/exceptions/source/modern/grouped_simple.py @@ -0,0 +1,41 @@ +import sys + +from loguru import logger + +logger.remove() +logger.add(sys.stderr, format="", diagnose=True, backtrace=True, colorize=False) + + +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/modern/grouped_with_cause_and_context.py b/tests/exceptions/source/modern/grouped_with_cause_and_context.py new file mode 100644 index 00000000..b6181445 --- /dev/null +++ b/tests/exceptions/source/modern/grouped_with_cause_and_context.py @@ -0,0 +1,43 @@ +import sys + +from loguru import logger + +logger.remove() +logger.add(sys.stderr, format="", diagnose=True, backtrace=True, colorize=False) + + +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..4607d3cf 100644 --- a/tests/test_exceptions_formatting.py +++ b/tests/test_exceptions_formatting.py @@ -224,3 +224,21 @@ 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_as_cause_and_context", (3, 11)), + ("grouped_max_length", (3, 11)), + ("grouped_max_depth", (3, 11)), + ], +) +def test_exception_modern(filename, minimum_python_version): + if sys.version_info < minimum_python_version: + pytest.skip("Feature not supported in this Python version") + + compare_exception("modern", filename)