2828from . import pretty
2929from ._loop import loop_first_last , loop_last
3030from .columns import Columns
31- from .console import Console , ConsoleOptions , ConsoleRenderable , RenderResult , group
31+ from .console import (
32+ Console ,
33+ ConsoleOptions ,
34+ ConsoleRenderable ,
35+ Group ,
36+ RenderResult ,
37+ group ,
38+ )
3239from .constrain import Constrain
3340from .highlighter import RegexHighlighter , ReprHighlighter
3441from .panel import Panel
@@ -128,26 +135,25 @@ def excepthook(
128135 value : BaseException ,
129136 traceback : Optional [TracebackType ],
130137 ) -> None :
131- traceback_console .print (
132- Traceback .from_exception (
133- type_ ,
134- value ,
135- traceback ,
136- width = width ,
137- code_width = code_width ,
138- extra_lines = extra_lines ,
139- theme = theme ,
140- word_wrap = word_wrap ,
141- show_locals = show_locals ,
142- locals_max_length = locals_max_length ,
143- locals_max_string = locals_max_string ,
144- locals_hide_dunder = locals_hide_dunder ,
145- locals_hide_sunder = bool (locals_hide_sunder ),
146- indent_guides = indent_guides ,
147- suppress = suppress ,
148- max_frames = max_frames ,
149- )
138+ exception_traceback = Traceback .from_exception (
139+ type_ ,
140+ value ,
141+ traceback ,
142+ width = width ,
143+ code_width = code_width ,
144+ extra_lines = extra_lines ,
145+ theme = theme ,
146+ word_wrap = word_wrap ,
147+ show_locals = show_locals ,
148+ locals_max_length = locals_max_length ,
149+ locals_max_string = locals_max_string ,
150+ locals_hide_dunder = locals_hide_dunder ,
151+ locals_hide_sunder = bool (locals_hide_sunder ),
152+ indent_guides = indent_guides ,
153+ suppress = suppress ,
154+ max_frames = max_frames ,
150155 )
156+ traceback_console .print (exception_traceback )
151157
152158 def ipy_excepthook_closure (ip : Any ) -> None : # pragma: no cover
153159 tb_data = {} # store information about showtraceback call
@@ -230,6 +236,8 @@ class Stack:
230236 is_cause : bool = False
231237 frames : List [Frame ] = field (default_factory = list )
232238 notes : List [str ] = field (default_factory = list )
239+ is_group : bool = False
240+ exceptions : List ["Trace" ] = field (default_factory = list )
233241
234242
235243@dataclass
@@ -450,6 +458,22 @@ def safe_str(_object: Any) -> str:
450458 notes = notes ,
451459 )
452460
461+ if sys .version_info >= (3 , 11 ):
462+ if isinstance (exc_value , (BaseExceptionGroup , ExceptionGroup )):
463+ stack .is_group = True
464+ for exception in exc_value .exceptions :
465+ stack .exceptions .append (
466+ Traceback .extract (
467+ type (exception ),
468+ exception ,
469+ exception .__traceback__ ,
470+ show_locals = show_locals ,
471+ locals_max_length = locals_max_length ,
472+ locals_hide_dunder = locals_hide_dunder ,
473+ locals_hide_sunder = locals_hide_sunder ,
474+ )
475+ )
476+
453477 if isinstance (exc_value , SyntaxError ):
454478 stack .syntax_error = _SyntaxError (
455479 offset = exc_value .offset or 0 ,
@@ -558,6 +582,7 @@ def get_locals(
558582 break # pragma: no cover
559583
560584 trace = Trace (stacks = stacks )
585+
561586 return trace
562587
563588 def __rich_console__ (
@@ -590,7 +615,9 @@ def __rich_console__(
590615 )
591616
592617 highlighter = ReprHighlighter ()
593- for last , stack in loop_last (reversed (self .trace .stacks )):
618+
619+ @group ()
620+ def render_stack (stack : Stack , last : bool ) -> RenderResult :
594621 if stack .frames :
595622 stack_renderable : ConsoleRenderable = Panel (
596623 self ._render_stack (stack ),
@@ -632,6 +659,21 @@ def __rich_console__(
632659 for note in stack .notes :
633660 yield Text .assemble (("[NOTE] " , "traceback.note" ), highlighter (note ))
634661
662+ if stack .is_group :
663+ for group_no , group_exception in enumerate (stack .exceptions , 1 ):
664+ grouped_exceptions : List [Group ] = []
665+ for group_last , group_stack in loop_last (group_exception .stacks ):
666+ grouped_exceptions .append (render_stack (group_stack , group_last ))
667+ yield ""
668+ yield Constrain (
669+ Panel (
670+ Group (* grouped_exceptions ),
671+ title = f"Sub-exception #{ group_no } " ,
672+ border_style = "traceback.group.border" ,
673+ ),
674+ self .width ,
675+ )
676+
635677 if not last :
636678 if stack .is_cause :
637679 yield Text .from_markup (
@@ -642,6 +684,9 @@ def __rich_console__(
642684 "\n [i]During handling of the above exception, another exception occurred:\n " ,
643685 )
644686
687+ for last , stack in loop_last (reversed (self .trace .stacks )):
688+ yield render_stack (stack , last )
689+
645690 @group ()
646691 def _render_syntax_error (self , syntax_error : _SyntaxError ) -> RenderResult :
647692 highlighter = ReprHighlighter ()
0 commit comments