28
28
from . import pretty
29
29
from ._loop import loop_first_last , loop_last
30
30
from .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
+ )
32
39
from .constrain import Constrain
33
40
from .highlighter import RegexHighlighter , ReprHighlighter
34
41
from .panel import Panel
@@ -128,26 +135,25 @@ def excepthook(
128
135
value : BaseException ,
129
136
traceback : Optional [TracebackType ],
130
137
) -> 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 ,
150
155
)
156
+ traceback_console .print (exception_traceback )
151
157
152
158
def ipy_excepthook_closure (ip : Any ) -> None : # pragma: no cover
153
159
tb_data = {} # store information about showtraceback call
@@ -230,6 +236,8 @@ class Stack:
230
236
is_cause : bool = False
231
237
frames : List [Frame ] = field (default_factory = list )
232
238
notes : List [str ] = field (default_factory = list )
239
+ is_group : bool = False
240
+ exceptions : List ["Trace" ] = field (default_factory = list )
233
241
234
242
235
243
@dataclass
@@ -450,6 +458,22 @@ def safe_str(_object: Any) -> str:
450
458
notes = notes ,
451
459
)
452
460
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
+
453
477
if isinstance (exc_value , SyntaxError ):
454
478
stack .syntax_error = _SyntaxError (
455
479
offset = exc_value .offset or 0 ,
@@ -558,6 +582,7 @@ def get_locals(
558
582
break # pragma: no cover
559
583
560
584
trace = Trace (stacks = stacks )
585
+
561
586
return trace
562
587
563
588
def __rich_console__ (
@@ -590,7 +615,9 @@ def __rich_console__(
590
615
)
591
616
592
617
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 :
594
621
if stack .frames :
595
622
stack_renderable : ConsoleRenderable = Panel (
596
623
self ._render_stack (stack ),
@@ -632,6 +659,21 @@ def __rich_console__(
632
659
for note in stack .notes :
633
660
yield Text .assemble (("[NOTE] " , "traceback.note" ), highlighter (note ))
634
661
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
+
635
677
if not last :
636
678
if stack .is_cause :
637
679
yield Text .from_markup (
@@ -642,6 +684,9 @@ def __rich_console__(
642
684
"\n [i]During handling of the above exception, another exception occurred:\n " ,
643
685
)
644
686
687
+ for last , stack in loop_last (reversed (self .trace .stacks )):
688
+ yield render_stack (stack , last )
689
+
645
690
@group ()
646
691
def _render_syntax_error (self , syntax_error : _SyntaxError ) -> RenderResult :
647
692
highlighter = ReprHighlighter ()
0 commit comments