1010import re
1111import socket
1212import sys
13+ import traceback
1314import types
15+ import typing
1416
1517def eprint (* args , ** kwargs ):
1618 print (* args , file = sys .stderr , ** kwargs )
@@ -29,6 +31,7 @@ def eprint(*args, **kwargs):
2931 str : "str" ,
3032 type (None ): "none" ,
3133 type : "type" ,
34+ typing .TypeAliasType : "type" ,
3235 types .FunctionType : "function"
3336}
3437HEAP_TYPES = {
@@ -120,6 +123,8 @@ def format(self):
120123 if search_result is not None :
121124 type_name = f"<class '{ search_result .group (1 )} '>"
122125 d ["value" ] = type_name
126+ elif type (d ["value" ]) == typing .TypeAliasType :
127+ d ["value" ] = "<TypeAlias>"
123128 elif inspect .isfunction (d ["value" ]):
124129 function_desc = str (d ["value" ])
125130 search_result = function_str_regex .search (function_desc )
@@ -249,15 +254,19 @@ class TraceStep:
249254 stack : Stack
250255 heap : Heap
251256 stdout : str
257+ traceback_text : str
252258
253259 def format (self ):
254- return {
260+ step = {
255261 "line" : self .line ,
256262 "filePath" : self .file_path ,
257263 "stack" : self .stack .format (),
258264 "heap" : self .heap .format (),
259265 "stdout" : self .stdout ,
260266 }
267+ if self .traceback_text is not None :
268+ step ["traceback" ] = self .traceback_text
269+ return step
261270
262271
263272def should_ignore (variable_name , value , script_path , ignore_list = []):
@@ -373,7 +382,8 @@ def trace_dispatch(self, frame, event, arg):
373382 self .import_following = import_regex .search (next_source_line ) is not None
374383
375384 display_return = event == "return" and self .last_event != "exception" and len (self .stack .frames ) > 1
376- if event == "line" or display_return :
385+ display_exception = event == "exception" and self .last_event != "return"
386+ if event == "line" or display_return or display_exception :
377387 for variable_name in frame .f_locals :
378388 if should_ignore_on_stack (variable_name , frame .f_locals [variable_name ], self .filename , self .stack_ignore ):
379389 continue
@@ -385,7 +395,15 @@ def trace_dispatch(self, frame, event, arg):
385395 heap = generate_heap (frame , self .filename , self .stack_ignore )
386396 accumulated_stdout = self .accumulated_stdout + self .captured_stdout .getvalue ()
387397
388- step = TraceStep (line , filename , copy .deepcopy (self .stack ), copy .deepcopy (heap ), accumulated_stdout )
398+ traceback_text = None
399+ if event == "exception" :
400+ exception_value = arg [1 ]
401+ traceback_text_tmp = io .StringIO ()
402+ traceback .print_exception (exception_value , limit = 0 , file = traceback_text_tmp )
403+ traceback_text = traceback_text_tmp .getvalue ()
404+
405+
406+ step = TraceStep (line , filename , copy .deepcopy (self .stack ), copy .deepcopy (heap ), accumulated_stdout , traceback_text )
389407
390408 is_annotation = next_source_line .startswith ("@" )
391409 should_display_step = not is_annotation
@@ -408,6 +426,10 @@ def trace_dispatch(self, frame, event, arg):
408426 self .shown_class_defs .append (filename , line )
409427 self .last_step_was_class = is_class_def
410428 self .prev_num_frames = num_frames
429+
430+ if event == "exception" :
431+ # Terminate visualization after first exception in user code
432+ self .set_quit ()
411433 if event == "call" :
412434 self .stack .push_frame (frame )
413435 self .shown_class_defs .push_frame ()
0 commit comments