Skip to content

Commit aa36155

Browse files
committed
Show full stack trace on exception with __cause__ or __context__. Fixes microsoft#391
1 parent 59ee2ed commit aa36155

10 files changed

Lines changed: 2426 additions & 2079 deletions

File tree

src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py

Lines changed: 74 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
from _pydevd_bundle.pydevd_thread_lifecycle import pydevd_find_thread_by_id, resume_threads
8888
from _pydevd_bundle.pydevd_dont_trace_files import PYDEV_FILE
8989
import dis
90+
from _pydevd_bundle.pydevd_frame_utils import create_frames_list_from_exception_cause
9091
try:
9192
from urllib import quote_plus, unquote_plus # @UnresolvedImport
9293
except:
@@ -1196,62 +1197,86 @@ def build_exception_info_response(dbg, thread_id, request_seq, set_additional_th
11961197
additional_info = set_additional_thread_info(thread)
11971198
topmost_frame = additional_info.get_topmost_frame(thread)
11981199

1199-
frames = []
1200-
exc_type = None
1201-
exc_desc = None
12021200
current_paused_frame_name = ''
1203-
if topmost_frame is not None:
1204-
try:
1205-
frames_list = dbg.suspended_frames_manager.get_frames_list(thread_id)
1206-
if frames_list is not None:
1207-
exc_type = frames_list.exc_type
1208-
exc_desc = frames_list.exc_desc
1209-
trace_obj = frames_list.trace_obj
1210-
for frame_id, frame, method_name, original_filename, filename_in_utf8, lineno, _applied_mapping, show_as_current_frame in \
1211-
iter_visible_frames_info(dbg, frames_list):
1212-
1213-
line_text = linecache.getline(original_filename, lineno)
1214-
1215-
# Never filter out plugin frames!
1216-
if not getattr(frame, 'IS_PLUGIN_FRAME', False):
1217-
if dbg.is_files_filter_enabled and dbg.apply_files_filter(frame, original_filename, False):
1218-
continue
12191201

1220-
if show_as_current_frame:
1221-
current_paused_frame_name = method_name
1222-
method_name += ' (Current frame)'
1223-
frames.append((filename_in_utf8, lineno, method_name, line_text))
1224-
finally:
1225-
topmost_frame = None
1202+
source_path = '' # This is an extra bit of data used by Visual Studio
1203+
stack_str_lst = []
1204+
name = None
1205+
description = None
12261206

1227-
name = 'exception: type unknown'
1228-
if exc_type is not None:
1207+
if topmost_frame is not None:
12291208
try:
1230-
name = exc_type.__qualname__
1231-
except:
12321209
try:
1233-
name = exc_type.__name__
1210+
frames_list = dbg.suspended_frames_manager.get_frames_list(thread_id)
1211+
memo = set()
1212+
while frames_list is not None and len(frames_list):
1213+
frames = []
1214+
1215+
frame = None
1216+
1217+
if not name:
1218+
exc_type = frames_list.exc_type
1219+
if exc_type is not None:
1220+
try:
1221+
name = exc_type.__qualname__
1222+
except:
1223+
try:
1224+
name = exc_type.__name__
1225+
except:
1226+
try:
1227+
name = str(exc_type)
1228+
except:
1229+
pass
1230+
1231+
if not description:
1232+
exc_desc = frames_list.exc_desc
1233+
if exc_desc is not None:
1234+
try:
1235+
description = str(exc_desc)
1236+
except:
1237+
pass
1238+
1239+
for frame_id, frame, method_name, original_filename, filename_in_utf8, lineno, _applied_mapping, show_as_current_frame in \
1240+
iter_visible_frames_info(dbg, frames_list):
1241+
1242+
line_text = linecache.getline(original_filename, lineno)
1243+
1244+
# Never filter out plugin frames!
1245+
if not getattr(frame, 'IS_PLUGIN_FRAME', False):
1246+
if dbg.is_files_filter_enabled and dbg.apply_files_filter(frame, original_filename, False):
1247+
continue
1248+
1249+
if show_as_current_frame:
1250+
current_paused_frame_name = method_name
1251+
method_name += ' (Current frame)'
1252+
frames.append((filename_in_utf8, lineno, method_name, line_text))
1253+
1254+
if not source_path and frames:
1255+
source_path = frames[0][0]
1256+
1257+
stack_str = ''.join(traceback.format_list(frames[-max_frames:]))
1258+
stack_str += frames_list.exc_context_msg
1259+
stack_str_lst.append(stack_str)
1260+
1261+
frames_list = create_frames_list_from_exception_cause(
1262+
frames_list.trace_obj, None, frames_list.exc_type, frames_list.exc_desc, memo)
1263+
if frames_list is None or not frames_list:
1264+
break
1265+
12341266
except:
1235-
try:
1236-
name = str(exc_type)
1237-
except:
1238-
pass
1267+
pydev_log.exception('Error on build_exception_info_response.')
1268+
finally:
1269+
topmost_frame = None
1270+
full_stack_str = ''.join(reversed(stack_str_lst))
12391271

1240-
description = 'exception: no description'
1241-
if exc_desc is not None:
1242-
try:
1243-
description = str(exc_desc)
1244-
except:
1245-
pass
1272+
if not name:
1273+
name = 'exception: type unknown'
1274+
if not description:
1275+
description = 'exception: no description'
12461276

12471277
if current_paused_frame_name:
12481278
name += ' (note: full exception trace is shown but execution is paused at: %s)' % (current_paused_frame_name,)
12491279

1250-
stack_str = ''.join(traceback.format_list(frames[-max_frames:]))
1251-
1252-
# This is an extra bit of data used by Visual Studio
1253-
source_path = frames[0][0] if frames else ''
1254-
12551280
if thread.stop_reason == CMD_STEP_CAUGHT_EXCEPTION:
12561281
break_mode = pydevd_schema.ExceptionBreakMode.ALWAYS
12571282
else:
@@ -1268,8 +1293,10 @@ def build_exception_info_response(dbg, thread_id, request_seq, set_additional_th
12681293
details=pydevd_schema.ExceptionDetails(
12691294
message=description,
12701295
typeName=name,
1271-
stackTrace=stack_str,
1272-
source=source_path
1296+
stackTrace=full_stack_str,
1297+
source=source_path,
1298+
# Note: ExceptionDetails actually accepts an 'innerException', but
1299+
# when passing it, VSCode is not showing the stack trace at all.
12731300
)
12741301
)
12751302
)

0 commit comments

Comments
 (0)