mirror of
https://github.com/microsoft/debugpy.git
synced 2025-12-23 08:48:12 +00:00
Show the full stack trace on user unhandled exceptions. Fixes #399
This commit is contained in:
parent
d68a17170c
commit
3d472df2d6
16 changed files with 5105 additions and 4999 deletions
|
|
@ -1182,6 +1182,7 @@ def build_exception_info_response(dbg, thread_id, request_seq, set_additional_th
|
|||
frames = []
|
||||
exc_type = None
|
||||
exc_desc = None
|
||||
current_paused_frame_name = ''
|
||||
if topmost_frame is not None:
|
||||
try:
|
||||
frames_list = dbg.suspended_frames_manager.get_frames_list(thread_id)
|
||||
|
|
@ -1189,8 +1190,8 @@ def build_exception_info_response(dbg, thread_id, request_seq, set_additional_th
|
|||
exc_type = frames_list.exc_type
|
||||
exc_desc = frames_list.exc_desc
|
||||
trace_obj = frames_list.trace_obj
|
||||
for frame_id, frame, method_name, original_filename, filename_in_utf8, lineno, _applied_mapping in iter_visible_frames_info(
|
||||
dbg, frames_list):
|
||||
for frame_id, frame, method_name, original_filename, filename_in_utf8, lineno, _applied_mapping, show_as_current_frame in \
|
||||
iter_visible_frames_info(dbg, frames_list):
|
||||
|
||||
line_text = linecache.getline(original_filename, lineno)
|
||||
|
||||
|
|
@ -1198,6 +1199,10 @@ def build_exception_info_response(dbg, thread_id, request_seq, set_additional_th
|
|||
if not getattr(frame, 'IS_PLUGIN_FRAME', False):
|
||||
if dbg.is_files_filter_enabled and dbg.apply_files_filter(frame, original_filename, False):
|
||||
continue
|
||||
|
||||
if show_as_current_frame:
|
||||
current_paused_frame_name = method_name
|
||||
method_name += ' (Current frame)'
|
||||
frames.append((filename_in_utf8, lineno, method_name, line_text))
|
||||
finally:
|
||||
topmost_frame = None
|
||||
|
|
@ -1222,6 +1227,9 @@ def build_exception_info_response(dbg, thread_id, request_seq, set_additional_th
|
|||
except:
|
||||
pass
|
||||
|
||||
if current_paused_frame_name:
|
||||
name += ' (note: full exception trace is shown but execution is paused at: %s)' % (current_paused_frame_name,)
|
||||
|
||||
stack_str = ''.join(traceback.format_list(frames[-max_frames:]))
|
||||
|
||||
# This is an extra bit of data used by Visual Studio
|
||||
|
|
|
|||
|
|
@ -78,6 +78,8 @@ CMD_THREAD_RESUME_SINGLE_NOTIFICATION = 158
|
|||
CMD_STEP_OVER_MY_CODE = 159
|
||||
CMD_STEP_RETURN_MY_CODE = 160
|
||||
|
||||
CMD_SET_PY_EXCEPTION_JSON = 161
|
||||
|
||||
CMD_REDIRECT_OUTPUT = 200
|
||||
CMD_GET_NEXT_STATEMENT_TARGETS = 201
|
||||
CMD_SET_PROJECT_ROOTS = 202
|
||||
|
|
@ -171,6 +173,8 @@ ID_TO_MEANING = {
|
|||
'159': 'CMD_STEP_OVER_MY_CODE',
|
||||
'160': 'CMD_STEP_RETURN_MY_CODE',
|
||||
|
||||
'161': 'CMD_SET_PY_EXCEPTION_JSON',
|
||||
|
||||
'200': 'CMD_REDIRECT_OUTPUT',
|
||||
'201': 'CMD_GET_NEXT_STATEMENT_TARGETS',
|
||||
'202': 'CMD_SET_PROJECT_ROOTS',
|
||||
|
|
|
|||
|
|
@ -313,6 +313,10 @@ PYDEVD_UNBLOCK_THREADS_TIMEOUT = as_float_in_env('PYDEVD_UNBLOCK_THREADS_TIMEOUT
|
|||
# on how the thread interruption works (there are some caveats related to it).
|
||||
PYDEVD_INTERRUPT_THREAD_TIMEOUT = as_float_in_env('PYDEVD_INTERRUPT_THREAD_TIMEOUT', -1)
|
||||
|
||||
EXCEPTION_TYPE_UNHANDLED = 'UNHANDLED'
|
||||
EXCEPTION_TYPE_USER_UNHANDLED = 'USER_UNHANDLED'
|
||||
EXCEPTION_TYPE_HANDLED = 'HANDLED'
|
||||
|
||||
if SHOW_DEBUG_INFO_ENV:
|
||||
# show debug info before the debugger start
|
||||
DebugInfoHolder.DEBUG_RECORD_SOCKET_READS = True
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -137,7 +137,8 @@ import re
|
|||
|
||||
from _pydev_bundle import pydev_log
|
||||
from _pydevd_bundle import pydevd_dont_trace
|
||||
from _pydevd_bundle.pydevd_constants import (dict_iter_values, IS_PY3K, RETURN_VALUES_DICT, NO_FTRACE)
|
||||
from _pydevd_bundle.pydevd_constants import (dict_iter_values, IS_PY3K, RETURN_VALUES_DICT, NO_FTRACE,
|
||||
EXCEPTION_TYPE_HANDLED, EXCEPTION_TYPE_USER_UNHANDLED)
|
||||
from _pydevd_bundle.pydevd_frame_utils import add_exception_to_frame, just_raised, remove_exception_from_frame, ignore_exception_trace
|
||||
from _pydevd_bundle.pydevd_utils import get_clsname_for_code
|
||||
from pydevd_file_utils import get_abs_path_real_path_and_base_from_frame
|
||||
|
|
@ -286,7 +287,7 @@ cdef class PyDBFrame:
|
|||
should_stop, frame = self.should_stop_on_exception(frame, event, arg)
|
||||
|
||||
if should_stop:
|
||||
if self.handle_exception(frame, event, arg):
|
||||
if self.handle_exception(frame, event, arg, EXCEPTION_TYPE_HANDLED):
|
||||
return self.trace_dispatch
|
||||
|
||||
elif event == 'return':
|
||||
|
|
@ -336,79 +337,85 @@ cdef class PyDBFrame:
|
|||
pydev_log.exception()
|
||||
|
||||
if not should_stop:
|
||||
was_just_raised = trace.tb_next is None
|
||||
# Apply checks that don't need the exception breakpoint (where we shouldn't ever stop).
|
||||
if exception == SystemExit and main_debugger.ignore_system_exit_code(value):
|
||||
pass
|
||||
|
||||
# It was not handled by any plugin, lets check exception breakpoints.
|
||||
check_excs = []
|
||||
exc_break_caught = main_debugger.get_exception_breakpoint(
|
||||
exception, main_debugger.break_on_caught_exceptions)
|
||||
if exc_break_caught is not None:
|
||||
check_excs.append((exc_break_caught, False))
|
||||
elif exception in (GeneratorExit, StopIteration):
|
||||
# These exceptions are control-flow related (they work as a generator
|
||||
# pause), so, we shouldn't stop on them.
|
||||
pass
|
||||
|
||||
exc_break_user = main_debugger.get_exception_breakpoint(
|
||||
exception, main_debugger.break_on_user_uncaught_exceptions)
|
||||
if exc_break_user is not None:
|
||||
check_excs.append((exc_break_user, True))
|
||||
elif ignore_exception_trace(trace):
|
||||
pass
|
||||
|
||||
for exc_break, is_user_uncaught in check_excs:
|
||||
# Initially mark that it should stop and then go into exclusions.
|
||||
should_stop = True
|
||||
else:
|
||||
was_just_raised = trace.tb_next is None
|
||||
|
||||
if exception is SystemExit and main_debugger.ignore_system_exit_code(value):
|
||||
should_stop = False
|
||||
# It was not handled by any plugin, lets check exception breakpoints.
|
||||
check_excs = []
|
||||
|
||||
elif exception in (GeneratorExit, StopIteration):
|
||||
# These exceptions are control-flow related (they work as a generator
|
||||
# pause), so, we shouldn't stop on them.
|
||||
should_stop = False
|
||||
# Note: check user unhandled before regular exceptions.
|
||||
exc_break_user = main_debugger.get_exception_breakpoint(
|
||||
exception, main_debugger.break_on_user_uncaught_exceptions)
|
||||
if exc_break_user is not None:
|
||||
check_excs.append((exc_break_user, True))
|
||||
|
||||
elif main_debugger.exclude_exception_by_filter(exc_break, trace):
|
||||
pydev_log.debug("Ignore exception %s in library %s -- (%s)" % (exception, frame.f_code.co_filename, frame.f_code.co_name))
|
||||
should_stop = False
|
||||
exc_break_caught = main_debugger.get_exception_breakpoint(
|
||||
exception, main_debugger.break_on_caught_exceptions)
|
||||
if exc_break_caught is not None:
|
||||
check_excs.append((exc_break_caught, False))
|
||||
|
||||
elif ignore_exception_trace(trace):
|
||||
should_stop = False
|
||||
for exc_break, is_user_uncaught in check_excs:
|
||||
# Initially mark that it should stop and then go into exclusions.
|
||||
should_stop = True
|
||||
|
||||
elif exc_break.condition is not None and \
|
||||
not main_debugger.handle_breakpoint_condition(info, exc_break, frame):
|
||||
should_stop = False
|
||||
if main_debugger.exclude_exception_by_filter(exc_break, trace):
|
||||
pydev_log.debug("Ignore exception %s in library %s -- (%s)" % (exception, frame.f_code.co_filename, frame.f_code.co_name))
|
||||
should_stop = False
|
||||
|
||||
elif was_just_raised and main_debugger.skip_on_exceptions_thrown_in_same_context:
|
||||
# Option: Don't break if an exception is caught in the same function from which it is thrown
|
||||
should_stop = False
|
||||
elif exc_break.condition is not None and \
|
||||
not main_debugger.handle_breakpoint_condition(info, exc_break, frame):
|
||||
should_stop = False
|
||||
|
||||
elif exc_break.notify_on_first_raise_only and main_debugger.skip_on_exceptions_thrown_in_same_context \
|
||||
and not was_just_raised and not just_raised(trace.tb_next):
|
||||
# In this case we never stop if it was just raised, so, to know if it was the first we
|
||||
# need to check if we're in the 2nd method.
|
||||
should_stop = False # I.e.: we stop only when we're at the caller of a method that throws an exception
|
||||
elif is_user_uncaught:
|
||||
# Note: we don't stop here, we just collect the exc_info to use later on...
|
||||
should_stop = False
|
||||
if not main_debugger.apply_files_filter(frame, frame.f_code.co_filename, True) \
|
||||
and (frame.f_back is None or main_debugger.apply_files_filter(frame.f_back, frame.f_back.f_code.co_filename, True)):
|
||||
# User uncaught means that we're currently in user code but the code
|
||||
# up the stack is library code.
|
||||
exc_info = self.exc_info
|
||||
if not exc_info:
|
||||
exc_info = (arg, frame.f_lineno, set([frame.f_lineno]))
|
||||
else:
|
||||
lines = exc_info[2]
|
||||
lines.add(frame.f_lineno)
|
||||
exc_info = (arg, frame.f_lineno, lines)
|
||||
self.exc_info = exc_info
|
||||
else:
|
||||
# I.e.: these are only checked if we're not dealing with user uncaught exceptions.
|
||||
if exc_break.notify_on_first_raise_only and main_debugger.skip_on_exceptions_thrown_in_same_context \
|
||||
and not was_just_raised and not just_raised(trace.tb_next):
|
||||
# In this case we never stop if it was just raised, so, to know if it was the first we
|
||||
# need to check if we're in the 2nd method.
|
||||
should_stop = False # I.e.: we stop only when we're at the caller of a method that throws an exception
|
||||
|
||||
elif exc_break.notify_on_first_raise_only and not main_debugger.skip_on_exceptions_thrown_in_same_context \
|
||||
and not was_just_raised:
|
||||
should_stop = False # I.e.: we stop only when it was just raised
|
||||
elif exc_break.notify_on_first_raise_only and not main_debugger.skip_on_exceptions_thrown_in_same_context \
|
||||
and not was_just_raised:
|
||||
should_stop = False # I.e.: we stop only when it was just raised
|
||||
|
||||
elif is_user_uncaught:
|
||||
should_stop = False
|
||||
if not main_debugger.apply_files_filter(frame, frame.f_code.co_filename, True) \
|
||||
and (frame.f_back is None or main_debugger.apply_files_filter(frame.f_back, frame.f_back.f_code.co_filename, True)):
|
||||
# User uncaught means that we're currently in user code but the code
|
||||
# up the stack is library code.
|
||||
exc_info = self.exc_info
|
||||
if not exc_info:
|
||||
exc_info = (arg, frame.f_lineno, set([frame.f_lineno]))
|
||||
else:
|
||||
lines = exc_info[2]
|
||||
lines.add(frame.f_lineno)
|
||||
exc_info = (arg, frame.f_lineno, lines)
|
||||
self.exc_info = exc_info
|
||||
elif was_just_raised and main_debugger.skip_on_exceptions_thrown_in_same_context:
|
||||
# Option: Don't break if an exception is caught in the same function from which it is thrown
|
||||
should_stop = False
|
||||
|
||||
if should_stop:
|
||||
exception_breakpoint = exc_break
|
||||
try:
|
||||
info.pydev_message = exc_break.qname
|
||||
except:
|
||||
info.pydev_message = exc_break.qname.encode('utf-8')
|
||||
break
|
||||
if should_stop:
|
||||
exception_breakpoint = exc_break
|
||||
try:
|
||||
info.pydev_message = exc_break.qname
|
||||
except:
|
||||
info.pydev_message = exc_break.qname.encode('utf-8')
|
||||
break
|
||||
|
||||
if should_stop:
|
||||
# Always add exception to frame (must remove later after we proceed).
|
||||
|
|
@ -422,11 +429,11 @@ cdef class PyDBFrame:
|
|||
def handle_user_exception(self, frame):
|
||||
exc_info = self.exc_info
|
||||
if exc_info:
|
||||
return self.handle_exception(frame, 'exception', exc_info[0])
|
||||
return self.handle_exception(frame, 'exception', exc_info[0], EXCEPTION_TYPE_USER_UNHANDLED)
|
||||
return False
|
||||
|
||||
# IFDEF CYTHON -- DONT EDIT THIS FILE (it is automatically generated)
|
||||
cpdef handle_exception(self, frame, str event, arg):
|
||||
cpdef handle_exception(self, frame, str event, arg, str exception_type):
|
||||
cdef bint stopped;
|
||||
cdef tuple abs_real_path_and_base;
|
||||
cdef str absolute_filename;
|
||||
|
|
@ -438,7 +445,7 @@ cdef class PyDBFrame:
|
|||
cdef object trace_obj;
|
||||
cdef object main_debugger;
|
||||
# ELSE
|
||||
# def handle_exception(self, frame, event, arg):
|
||||
# def handle_exception(self, frame, event, arg, exception_type):
|
||||
# ENDIF
|
||||
stopped = False
|
||||
try:
|
||||
|
|
@ -532,7 +539,7 @@ cdef class PyDBFrame:
|
|||
stopped = True
|
||||
main_debugger.send_caught_exception_stack(thread, arg, id(frame))
|
||||
self.set_suspend(thread, 137)
|
||||
self.do_wait_suspend(thread, frame, event, arg)
|
||||
self.do_wait_suspend(thread, frame, event, arg, exception_type=exception_type)
|
||||
main_debugger.send_caught_exception_stack_proceeded(thread)
|
||||
except:
|
||||
pydev_log.exception()
|
||||
|
|
@ -746,7 +753,7 @@ cdef class PyDBFrame:
|
|||
if has_exception_breakpoints:
|
||||
should_stop, frame = self.should_stop_on_exception(frame, event, arg)
|
||||
if should_stop:
|
||||
if self.handle_exception(frame, event, arg):
|
||||
if self.handle_exception(frame, event, arg, EXCEPTION_TYPE_HANDLED):
|
||||
return self.trace_dispatch
|
||||
|
||||
return self.trace_dispatch
|
||||
|
|
@ -796,7 +803,7 @@ cdef class PyDBFrame:
|
|||
if has_exception_breakpoints:
|
||||
should_stop, frame = self.should_stop_on_exception(frame, event, arg)
|
||||
if should_stop:
|
||||
if self.handle_exception(frame, event, arg):
|
||||
if self.handle_exception(frame, event, arg, EXCEPTION_TYPE_HANDLED):
|
||||
return self.trace_dispatch
|
||||
is_line = False
|
||||
is_return = False
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ import re
|
|||
|
||||
from _pydev_bundle import pydev_log
|
||||
from _pydevd_bundle import pydevd_dont_trace
|
||||
from _pydevd_bundle.pydevd_constants import (dict_iter_values, IS_PY3K, RETURN_VALUES_DICT, NO_FTRACE)
|
||||
from _pydevd_bundle.pydevd_constants import (dict_iter_values, IS_PY3K, RETURN_VALUES_DICT, NO_FTRACE,
|
||||
EXCEPTION_TYPE_HANDLED, EXCEPTION_TYPE_USER_UNHANDLED)
|
||||
from _pydevd_bundle.pydevd_frame_utils import add_exception_to_frame, just_raised, remove_exception_from_frame, ignore_exception_trace
|
||||
from _pydevd_bundle.pydevd_utils import get_clsname_for_code
|
||||
from pydevd_file_utils import get_abs_path_real_path_and_base_from_frame
|
||||
|
|
@ -165,7 +166,7 @@ class PyDBFrame:
|
|||
should_stop, frame = self.should_stop_on_exception(frame, event, arg)
|
||||
|
||||
if should_stop:
|
||||
if self.handle_exception(frame, event, arg):
|
||||
if self.handle_exception(frame, event, arg, EXCEPTION_TYPE_HANDLED):
|
||||
return self.trace_dispatch
|
||||
|
||||
elif event == 'return':
|
||||
|
|
@ -215,79 +216,85 @@ class PyDBFrame:
|
|||
pydev_log.exception()
|
||||
|
||||
if not should_stop:
|
||||
was_just_raised = trace.tb_next is None
|
||||
# Apply checks that don't need the exception breakpoint (where we shouldn't ever stop).
|
||||
if exception == SystemExit and main_debugger.ignore_system_exit_code(value):
|
||||
pass
|
||||
|
||||
# It was not handled by any plugin, lets check exception breakpoints.
|
||||
check_excs = []
|
||||
exc_break_caught = main_debugger.get_exception_breakpoint(
|
||||
exception, main_debugger.break_on_caught_exceptions)
|
||||
if exc_break_caught is not None:
|
||||
check_excs.append((exc_break_caught, False))
|
||||
elif exception in (GeneratorExit, StopIteration):
|
||||
# These exceptions are control-flow related (they work as a generator
|
||||
# pause), so, we shouldn't stop on them.
|
||||
pass
|
||||
|
||||
exc_break_user = main_debugger.get_exception_breakpoint(
|
||||
exception, main_debugger.break_on_user_uncaught_exceptions)
|
||||
if exc_break_user is not None:
|
||||
check_excs.append((exc_break_user, True))
|
||||
elif ignore_exception_trace(trace):
|
||||
pass
|
||||
|
||||
for exc_break, is_user_uncaught in check_excs:
|
||||
# Initially mark that it should stop and then go into exclusions.
|
||||
should_stop = True
|
||||
else:
|
||||
was_just_raised = trace.tb_next is None
|
||||
|
||||
if exception is SystemExit and main_debugger.ignore_system_exit_code(value):
|
||||
should_stop = False
|
||||
# It was not handled by any plugin, lets check exception breakpoints.
|
||||
check_excs = []
|
||||
|
||||
elif exception in (GeneratorExit, StopIteration):
|
||||
# These exceptions are control-flow related (they work as a generator
|
||||
# pause), so, we shouldn't stop on them.
|
||||
should_stop = False
|
||||
# Note: check user unhandled before regular exceptions.
|
||||
exc_break_user = main_debugger.get_exception_breakpoint(
|
||||
exception, main_debugger.break_on_user_uncaught_exceptions)
|
||||
if exc_break_user is not None:
|
||||
check_excs.append((exc_break_user, True))
|
||||
|
||||
elif main_debugger.exclude_exception_by_filter(exc_break, trace):
|
||||
pydev_log.debug("Ignore exception %s in library %s -- (%s)" % (exception, frame.f_code.co_filename, frame.f_code.co_name))
|
||||
should_stop = False
|
||||
exc_break_caught = main_debugger.get_exception_breakpoint(
|
||||
exception, main_debugger.break_on_caught_exceptions)
|
||||
if exc_break_caught is not None:
|
||||
check_excs.append((exc_break_caught, False))
|
||||
|
||||
elif ignore_exception_trace(trace):
|
||||
should_stop = False
|
||||
for exc_break, is_user_uncaught in check_excs:
|
||||
# Initially mark that it should stop and then go into exclusions.
|
||||
should_stop = True
|
||||
|
||||
elif exc_break.condition is not None and \
|
||||
not main_debugger.handle_breakpoint_condition(info, exc_break, frame):
|
||||
should_stop = False
|
||||
if main_debugger.exclude_exception_by_filter(exc_break, trace):
|
||||
pydev_log.debug("Ignore exception %s in library %s -- (%s)" % (exception, frame.f_code.co_filename, frame.f_code.co_name))
|
||||
should_stop = False
|
||||
|
||||
elif was_just_raised and main_debugger.skip_on_exceptions_thrown_in_same_context:
|
||||
# Option: Don't break if an exception is caught in the same function from which it is thrown
|
||||
should_stop = False
|
||||
elif exc_break.condition is not None and \
|
||||
not main_debugger.handle_breakpoint_condition(info, exc_break, frame):
|
||||
should_stop = False
|
||||
|
||||
elif exc_break.notify_on_first_raise_only and main_debugger.skip_on_exceptions_thrown_in_same_context \
|
||||
and not was_just_raised and not just_raised(trace.tb_next):
|
||||
# In this case we never stop if it was just raised, so, to know if it was the first we
|
||||
# need to check if we're in the 2nd method.
|
||||
should_stop = False # I.e.: we stop only when we're at the caller of a method that throws an exception
|
||||
elif is_user_uncaught:
|
||||
# Note: we don't stop here, we just collect the exc_info to use later on...
|
||||
should_stop = False
|
||||
if not main_debugger.apply_files_filter(frame, frame.f_code.co_filename, True) \
|
||||
and (frame.f_back is None or main_debugger.apply_files_filter(frame.f_back, frame.f_back.f_code.co_filename, True)):
|
||||
# User uncaught means that we're currently in user code but the code
|
||||
# up the stack is library code.
|
||||
exc_info = self.exc_info
|
||||
if not exc_info:
|
||||
exc_info = (arg, frame.f_lineno, set([frame.f_lineno]))
|
||||
else:
|
||||
lines = exc_info[2]
|
||||
lines.add(frame.f_lineno)
|
||||
exc_info = (arg, frame.f_lineno, lines)
|
||||
self.exc_info = exc_info
|
||||
else:
|
||||
# I.e.: these are only checked if we're not dealing with user uncaught exceptions.
|
||||
if exc_break.notify_on_first_raise_only and main_debugger.skip_on_exceptions_thrown_in_same_context \
|
||||
and not was_just_raised and not just_raised(trace.tb_next):
|
||||
# In this case we never stop if it was just raised, so, to know if it was the first we
|
||||
# need to check if we're in the 2nd method.
|
||||
should_stop = False # I.e.: we stop only when we're at the caller of a method that throws an exception
|
||||
|
||||
elif exc_break.notify_on_first_raise_only and not main_debugger.skip_on_exceptions_thrown_in_same_context \
|
||||
and not was_just_raised:
|
||||
should_stop = False # I.e.: we stop only when it was just raised
|
||||
elif exc_break.notify_on_first_raise_only and not main_debugger.skip_on_exceptions_thrown_in_same_context \
|
||||
and not was_just_raised:
|
||||
should_stop = False # I.e.: we stop only when it was just raised
|
||||
|
||||
elif is_user_uncaught:
|
||||
should_stop = False
|
||||
if not main_debugger.apply_files_filter(frame, frame.f_code.co_filename, True) \
|
||||
and (frame.f_back is None or main_debugger.apply_files_filter(frame.f_back, frame.f_back.f_code.co_filename, True)):
|
||||
# User uncaught means that we're currently in user code but the code
|
||||
# up the stack is library code.
|
||||
exc_info = self.exc_info
|
||||
if not exc_info:
|
||||
exc_info = (arg, frame.f_lineno, set([frame.f_lineno]))
|
||||
else:
|
||||
lines = exc_info[2]
|
||||
lines.add(frame.f_lineno)
|
||||
exc_info = (arg, frame.f_lineno, lines)
|
||||
self.exc_info = exc_info
|
||||
elif was_just_raised and main_debugger.skip_on_exceptions_thrown_in_same_context:
|
||||
# Option: Don't break if an exception is caught in the same function from which it is thrown
|
||||
should_stop = False
|
||||
|
||||
if should_stop:
|
||||
exception_breakpoint = exc_break
|
||||
try:
|
||||
info.pydev_message = exc_break.qname
|
||||
except:
|
||||
info.pydev_message = exc_break.qname.encode('utf-8')
|
||||
break
|
||||
if should_stop:
|
||||
exception_breakpoint = exc_break
|
||||
try:
|
||||
info.pydev_message = exc_break.qname
|
||||
except:
|
||||
info.pydev_message = exc_break.qname.encode('utf-8')
|
||||
break
|
||||
|
||||
if should_stop:
|
||||
# Always add exception to frame (must remove later after we proceed).
|
||||
|
|
@ -301,11 +308,11 @@ class PyDBFrame:
|
|||
def handle_user_exception(self, frame):
|
||||
exc_info = self.exc_info
|
||||
if exc_info:
|
||||
return self.handle_exception(frame, 'exception', exc_info[0])
|
||||
return self.handle_exception(frame, 'exception', exc_info[0], EXCEPTION_TYPE_USER_UNHANDLED)
|
||||
return False
|
||||
|
||||
# IFDEF CYTHON
|
||||
# cpdef handle_exception(self, frame, str event, arg):
|
||||
# cpdef handle_exception(self, frame, str event, arg, str exception_type):
|
||||
# cdef bint stopped;
|
||||
# cdef tuple abs_real_path_and_base;
|
||||
# cdef str absolute_filename;
|
||||
|
|
@ -317,7 +324,7 @@ class PyDBFrame:
|
|||
# cdef object trace_obj;
|
||||
# cdef object main_debugger;
|
||||
# ELSE
|
||||
def handle_exception(self, frame, event, arg):
|
||||
def handle_exception(self, frame, event, arg, exception_type):
|
||||
# ENDIF
|
||||
stopped = False
|
||||
try:
|
||||
|
|
@ -411,7 +418,7 @@ class PyDBFrame:
|
|||
stopped = True
|
||||
main_debugger.send_caught_exception_stack(thread, arg, id(frame))
|
||||
self.set_suspend(thread, CMD_STEP_CAUGHT_EXCEPTION)
|
||||
self.do_wait_suspend(thread, frame, event, arg)
|
||||
self.do_wait_suspend(thread, frame, event, arg, exception_type=exception_type)
|
||||
main_debugger.send_caught_exception_stack_proceeded(thread)
|
||||
except:
|
||||
pydev_log.exception()
|
||||
|
|
@ -625,7 +632,7 @@ class PyDBFrame:
|
|||
if has_exception_breakpoints:
|
||||
should_stop, frame = self.should_stop_on_exception(frame, event, arg)
|
||||
if should_stop:
|
||||
if self.handle_exception(frame, event, arg):
|
||||
if self.handle_exception(frame, event, arg, EXCEPTION_TYPE_HANDLED):
|
||||
return self.trace_dispatch
|
||||
|
||||
return self.trace_dispatch
|
||||
|
|
@ -675,7 +682,7 @@ class PyDBFrame:
|
|||
if has_exception_breakpoints:
|
||||
should_stop, frame = self.should_stop_on_exception(frame, event, arg)
|
||||
if should_stop:
|
||||
if self.handle_exception(frame, event, arg):
|
||||
if self.handle_exception(frame, event, arg, EXCEPTION_TYPE_HANDLED):
|
||||
return self.trace_dispatch
|
||||
is_line = False
|
||||
is_return = False
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
from _pydevd_bundle.pydevd_constants import IS_PY3K
|
||||
from _pydevd_bundle.pydevd_constants import IS_PY3K, EXCEPTION_TYPE_USER_UNHANDLED, \
|
||||
EXCEPTION_TYPE_UNHANDLED
|
||||
from _pydev_bundle import pydev_log
|
||||
import sys
|
||||
|
||||
|
|
@ -89,9 +90,20 @@ class FramesList(object):
|
|||
self.exc_desc = None
|
||||
self.trace_obj = None
|
||||
|
||||
# This may be set to set the current frame (for the case where we have
|
||||
# an unhandled exception where we want to show the root bu we have a different
|
||||
# executing frame).
|
||||
self.current_frame = None
|
||||
|
||||
def append(self, frame):
|
||||
self._frames.append(frame)
|
||||
|
||||
def last_frame(self):
|
||||
return self._frames[-1]
|
||||
|
||||
def __len__(self):
|
||||
return len(self._frames)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self._frames)
|
||||
|
||||
|
|
@ -107,6 +119,9 @@ class FramesList(object):
|
|||
lst.append('\n trace_obj: ')
|
||||
lst.append(str(self.trace_obj))
|
||||
|
||||
lst.append('\n current_frame: ')
|
||||
lst.append(str(self.current_frame))
|
||||
|
||||
for frame in self._frames:
|
||||
lst.append('\n ')
|
||||
lst.append(repr(frame))
|
||||
|
|
@ -117,13 +132,18 @@ class FramesList(object):
|
|||
__str__ = __repr__
|
||||
|
||||
|
||||
def create_frames_list_from_traceback(trace_obj, frame, exc_type, exc_desc):
|
||||
def create_frames_list_from_traceback(trace_obj, frame, exc_type, exc_desc, exception_type=None):
|
||||
'''
|
||||
:param trace_obj:
|
||||
This is the traceback from which the list should be created.
|
||||
|
||||
:param frame:
|
||||
This is the first frame to be considered (i.e.: topmost frame).
|
||||
This is the first frame to be considered (i.e.: topmost frame). If None is passed, all
|
||||
the frames from the traceback are shown (so, None should be passed for unhandled exceptions).
|
||||
|
||||
:param exception_type:
|
||||
If this is an unhandled exception or user unhandled exception, we'll not trim the stack to create from the passed
|
||||
frame, rather, we'll just mark the frame in the frames list.
|
||||
'''
|
||||
lst = []
|
||||
|
||||
|
|
@ -141,7 +161,11 @@ def create_frames_list_from_traceback(trace_obj, frame, exc_type, exc_desc):
|
|||
frames_list = None
|
||||
|
||||
for tb_frame, tb_lineno in reversed(lst):
|
||||
if frames_list is None and (frame is tb_frame or frame is None):
|
||||
if frames_list is None and (
|
||||
(frame is tb_frame) or
|
||||
(frame is None) or
|
||||
(exception_type == EXCEPTION_TYPE_USER_UNHANDLED)
|
||||
):
|
||||
frames_list = FramesList()
|
||||
|
||||
if frames_list is not None:
|
||||
|
|
@ -157,6 +181,12 @@ def create_frames_list_from_traceback(trace_obj, frame, exc_type, exc_desc):
|
|||
frames_list.exc_desc = exc_desc
|
||||
frames_list.trace_obj = trace_obj
|
||||
|
||||
if exception_type == EXCEPTION_TYPE_USER_UNHANDLED:
|
||||
frames_list.current_frame = frame
|
||||
elif exception_type == EXCEPTION_TYPE_UNHANDLED:
|
||||
if len(frames_list) > 0:
|
||||
frames_list.current_frame = frames_list.last_frame()
|
||||
|
||||
return frames_list
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -215,7 +215,7 @@ class NetCommandFactoryJson(NetCommandFactory):
|
|||
else:
|
||||
frames_list = pydevd_frame_utils.create_frames_list_from_frame(topmost_frame)
|
||||
|
||||
for frame_id, frame, method_name, original_filename, filename_in_utf8, lineno, applied_mapping in self._iter_visible_frames_info(
|
||||
for frame_id, frame, method_name, original_filename, filename_in_utf8, lineno, applied_mapping, show_as_current_frame in self._iter_visible_frames_info(
|
||||
py_db, frames_list
|
||||
):
|
||||
|
||||
|
|
@ -235,6 +235,8 @@ class NetCommandFactoryJson(NetCommandFactory):
|
|||
presentation_hint = 'subtle'
|
||||
|
||||
formatted_name = self._format_frame_name(fmt, method_name, module_name, lineno, filename_in_utf8)
|
||||
if show_as_current_frame:
|
||||
formatted_name += ' (Current frame)'
|
||||
source_reference = pydevd_file_utils.get_client_filename_source_reference(filename_in_utf8)
|
||||
|
||||
if not source_reference and not applied_mapping and not os.path.exists(original_filename):
|
||||
|
|
|
|||
|
|
@ -161,6 +161,7 @@ class NetCommandFactory(object):
|
|||
def _iter_visible_frames_info(self, py_db, frames_list):
|
||||
assert frames_list.__class__ == FramesList
|
||||
for frame in frames_list:
|
||||
show_as_current_frame = frame is frames_list.current_frame
|
||||
if frame.f_code is None:
|
||||
pydev_log.info('Frame without f_code: %s', frame)
|
||||
continue # IronPython sometimes does not have it!
|
||||
|
|
@ -183,7 +184,7 @@ class NetCommandFactory(object):
|
|||
new_filename_in_utf8, applied_mapping = pydevd_file_utils.map_file_to_client(filename_in_utf8)
|
||||
applied_mapping = applied_mapping or changed
|
||||
|
||||
yield frame_id, frame, method_name, abs_path_real_path_and_base[0], new_filename_in_utf8, lineno, applied_mapping
|
||||
yield frame_id, frame, method_name, abs_path_real_path_and_base[0], new_filename_in_utf8, lineno, applied_mapping, show_as_current_frame
|
||||
|
||||
def make_thread_stack_str(self, py_db, frames_list):
|
||||
assert frames_list.__class__ == FramesList
|
||||
|
|
@ -192,7 +193,7 @@ class NetCommandFactory(object):
|
|||
append = cmd_text_list.append
|
||||
|
||||
try:
|
||||
for frame_id, frame, method_name, _original_filename, filename_in_utf8, lineno, _applied_mapping in self._iter_visible_frames_info(
|
||||
for frame_id, frame, method_name, _original_filename, filename_in_utf8, lineno, _applied_mapping, _show_as_current_frame in self._iter_visible_frames_info(
|
||||
py_db, frames_list
|
||||
):
|
||||
|
||||
|
|
|
|||
|
|
@ -333,72 +333,100 @@ class _PyDevCommandProcessor(object):
|
|||
thread_id, frame_id, scope, expression = text.split('\t', 3)
|
||||
self.api.request_console_exec(py_db, seq, thread_id, frame_id, expression)
|
||||
|
||||
def cmd_set_py_exception(self, py_db, cmd_id, seq, text):
|
||||
# Command which receives set of exceptions on which user wants to break the debugger
|
||||
# text is:
|
||||
#
|
||||
# break_on_uncaught;
|
||||
# break_on_caught;
|
||||
# skip_on_exceptions_thrown_in_same_context;
|
||||
# ignore_exceptions_thrown_in_lines_with_ignore_exception;
|
||||
# ignore_libraries;
|
||||
# TypeError;ImportError;zipimport.ZipImportError;
|
||||
#
|
||||
# i.e.: true;true;true;true;true;TypeError;ImportError;zipimport.ZipImportError;
|
||||
#
|
||||
def cmd_set_py_exception_json(self, py_db, cmd_id, seq, text):
|
||||
# This API is optional and works 'in bulk' -- it's possible
|
||||
# to get finer-grained control with CMD_ADD_EXCEPTION_BREAK/CMD_REMOVE_EXCEPTION_BREAK
|
||||
# which allows setting caught/uncaught per exception.
|
||||
splitted = text.split(';')
|
||||
py_db.break_on_uncaught_exceptions = {}
|
||||
py_db.break_on_caught_exceptions = {}
|
||||
py_db.break_on_user_uncaught_exceptions = {}
|
||||
if len(splitted) >= 5:
|
||||
if splitted[0] == 'true':
|
||||
break_on_uncaught = True
|
||||
else:
|
||||
break_on_uncaught = False
|
||||
# which allows setting caught/uncaught per exception, although global settings such as:
|
||||
# - skip_on_exceptions_thrown_in_same_context
|
||||
# - ignore_exceptions_thrown_in_lines_with_ignore_exception
|
||||
# must still be set through this API (before anything else as this clears all existing
|
||||
# exception breakpoints).
|
||||
try:
|
||||
py_db.break_on_uncaught_exceptions = {}
|
||||
py_db.break_on_caught_exceptions = {}
|
||||
py_db.break_on_user_uncaught_exceptions = {}
|
||||
|
||||
if splitted[1] == 'true':
|
||||
break_on_caught = True
|
||||
else:
|
||||
break_on_caught = False
|
||||
as_json = json.loads(text)
|
||||
break_on_uncaught = as_json.get('break_on_uncaught', False)
|
||||
break_on_caught = as_json.get('break_on_caught', False)
|
||||
break_on_user_caught = as_json.get('break_on_user_caught', False)
|
||||
py_db.skip_on_exceptions_thrown_in_same_context = as_json.get('skip_on_exceptions_thrown_in_same_context', False)
|
||||
py_db.ignore_exceptions_thrown_in_lines_with_ignore_exception = as_json.get('ignore_exceptions_thrown_in_lines_with_ignore_exception', False)
|
||||
ignore_libraries = as_json.get('ignore_libraries', False)
|
||||
exception_types = as_json.get('exception_types', [])
|
||||
|
||||
if splitted[2] == 'true':
|
||||
py_db.skip_on_exceptions_thrown_in_same_context = True
|
||||
else:
|
||||
py_db.skip_on_exceptions_thrown_in_same_context = False
|
||||
|
||||
if splitted[3] == 'true':
|
||||
py_db.ignore_exceptions_thrown_in_lines_with_ignore_exception = True
|
||||
else:
|
||||
py_db.ignore_exceptions_thrown_in_lines_with_ignore_exception = False
|
||||
|
||||
if splitted[4] == 'true':
|
||||
ignore_libraries = True
|
||||
else:
|
||||
ignore_libraries = False
|
||||
|
||||
for exception_type in splitted[5:]:
|
||||
exception_type = exception_type.strip()
|
||||
for exception_type in exception_types:
|
||||
if not exception_type:
|
||||
continue
|
||||
|
||||
exception_breakpoint = py_db.add_break_on_exception(
|
||||
py_db.add_break_on_exception(
|
||||
exception_type,
|
||||
condition=None,
|
||||
expression=None,
|
||||
notify_on_handled_exceptions=break_on_caught,
|
||||
notify_on_unhandled_exceptions=break_on_uncaught,
|
||||
notify_on_user_unhandled_exceptions=False, # TODO (not currently supported in this API).
|
||||
notify_on_user_unhandled_exceptions=break_on_user_caught,
|
||||
notify_on_first_raise_only=True,
|
||||
ignore_libraries=ignore_libraries,
|
||||
)
|
||||
|
||||
py_db.on_breakpoints_changed()
|
||||
py_db.on_breakpoints_changed()
|
||||
except:
|
||||
pydev_log.exception("Error when setting exception list. Received: %s", text)
|
||||
|
||||
else:
|
||||
sys.stderr.write("Error when setting exception list. Received: %s\n" % (text,))
|
||||
def cmd_set_py_exception(self, py_db, cmd_id, seq, text):
|
||||
# DEPRECATED. Use cmd_set_py_exception_json instead.
|
||||
try:
|
||||
splitted = text.split(';')
|
||||
py_db.break_on_uncaught_exceptions = {}
|
||||
py_db.break_on_caught_exceptions = {}
|
||||
py_db.break_on_user_uncaught_exceptions = {}
|
||||
if len(splitted) >= 5:
|
||||
if splitted[0] == 'true':
|
||||
break_on_uncaught = True
|
||||
else:
|
||||
break_on_uncaught = False
|
||||
|
||||
if splitted[1] == 'true':
|
||||
break_on_caught = True
|
||||
else:
|
||||
break_on_caught = False
|
||||
|
||||
if splitted[2] == 'true':
|
||||
py_db.skip_on_exceptions_thrown_in_same_context = True
|
||||
else:
|
||||
py_db.skip_on_exceptions_thrown_in_same_context = False
|
||||
|
||||
if splitted[3] == 'true':
|
||||
py_db.ignore_exceptions_thrown_in_lines_with_ignore_exception = True
|
||||
else:
|
||||
py_db.ignore_exceptions_thrown_in_lines_with_ignore_exception = False
|
||||
|
||||
if splitted[4] == 'true':
|
||||
ignore_libraries = True
|
||||
else:
|
||||
ignore_libraries = False
|
||||
|
||||
for exception_type in splitted[5:]:
|
||||
exception_type = exception_type.strip()
|
||||
if not exception_type:
|
||||
continue
|
||||
|
||||
py_db.add_break_on_exception(
|
||||
exception_type,
|
||||
condition=None,
|
||||
expression=None,
|
||||
notify_on_handled_exceptions=break_on_caught,
|
||||
notify_on_unhandled_exceptions=break_on_uncaught,
|
||||
notify_on_user_unhandled_exceptions=False, # TODO (not currently supported in this API).
|
||||
notify_on_first_raise_only=True,
|
||||
ignore_libraries=ignore_libraries,
|
||||
)
|
||||
else:
|
||||
pydev_log.exception("Expected to have at least 5 ';' separated items. Received: %s", text)
|
||||
|
||||
except:
|
||||
pydev_log.exception("Error when setting exception list. Received: %s", text)
|
||||
|
||||
def _load_source(self, py_db, cmd_id, seq, text):
|
||||
filename = text
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ from _pydevd_bundle.pydevd_constants import (IS_JYTH_LESS25, get_thread_id, get_
|
|||
dict_keys, dict_iter_items, DebugInfoHolder, PYTHON_SUSPEND, STATE_SUSPEND, STATE_RUN, get_frame,
|
||||
clear_cached_thread_id, INTERACTIVE_MODE_AVAILABLE, SHOW_DEBUG_INFO_ENV, IS_PY34_OR_GREATER, IS_PY2, NULL,
|
||||
NO_FTRACE, IS_IRONPYTHON, JSON_PROTOCOL, IS_CPYTHON, HTTP_JSON_PROTOCOL, USE_CUSTOM_SYS_CURRENT_FRAMES_MAP, call_only_once,
|
||||
ForkSafeLock, IGNORE_BASENAMES_STARTING_WITH)
|
||||
ForkSafeLock, IGNORE_BASENAMES_STARTING_WITH, EXCEPTION_TYPE_UNHANDLED)
|
||||
from _pydevd_bundle.pydevd_defaults import PydevdCustomization # Note: import alias used on pydev_monkey.
|
||||
from _pydevd_bundle.pydevd_custom_frames import CustomFramesContainer, custom_frames_container_init
|
||||
from _pydevd_bundle.pydevd_dont_trace_files import DONT_TRACE, PYDEV_FILE, LIB_FILE, DONT_TRACE_DIRS
|
||||
|
|
@ -1780,14 +1780,13 @@ class PyDB(object):
|
|||
""" returns a frame on the thread that has a given frame_id """
|
||||
return self.suspended_frames_manager.find_frame(thread_id, frame_id)
|
||||
|
||||
def do_wait_suspend(self, thread, frame, event, arg, is_unhandled_exception=False): # @UnusedVariable
|
||||
def do_wait_suspend(self, thread, frame, event, arg, exception_type=None): # @UnusedVariable
|
||||
""" busy waits until the thread state changes to RUN
|
||||
it expects thread's state as attributes of the thread.
|
||||
Upon running, processes any outstanding Stepping commands.
|
||||
|
||||
:param is_unhandled_exception:
|
||||
If True we should use the line of the exception instead of the current line in the frame
|
||||
as the paused location on the top-level frame (exception info must be passed on 'arg').
|
||||
:param exception_type:
|
||||
If pausing due to an exception, its type.
|
||||
"""
|
||||
if USE_CUSTOM_SYS_CURRENT_FRAMES_MAP:
|
||||
constructed_tid_to_last_frame[thread.ident] = sys._getframe()
|
||||
|
|
@ -1809,7 +1808,7 @@ class PyDB(object):
|
|||
# arg must be the exception info (tuple(exc_type, exc, traceback))
|
||||
exc_type, exc_desc, trace_obj = arg
|
||||
if trace_obj is not None:
|
||||
frames_list = pydevd_frame_utils.create_frames_list_from_traceback(trace_obj, frame, exc_type, exc_desc)
|
||||
frames_list = pydevd_frame_utils.create_frames_list_from_traceback(trace_obj, frame, exc_type, exc_desc, exception_type=exception_type)
|
||||
|
||||
if frames_list is None:
|
||||
frames_list = pydevd_frame_utils.create_frames_list_from_frame(frame)
|
||||
|
|
@ -1860,7 +1859,7 @@ class PyDB(object):
|
|||
if keep_suspended:
|
||||
# This means that we should pause again after a set next statement.
|
||||
self._threads_suspended_single_notification.increment_suspend_time()
|
||||
self.do_wait_suspend(thread, frame, event, arg, is_unhandled_exception)
|
||||
self.do_wait_suspend(thread, frame, event, arg, exception_type)
|
||||
if DebugInfoHolder.DEBUG_TRACE_LEVEL > 2:
|
||||
pydev_log.debug('Leaving PyDB.do_wait_suspend: %s (%s) %s', thread, thread_id, id(thread))
|
||||
|
||||
|
|
@ -1992,7 +1991,7 @@ class PyDB(object):
|
|||
try:
|
||||
add_exception_to_frame(frame, arg)
|
||||
self.set_suspend(thread, CMD_ADD_EXCEPTION_BREAK)
|
||||
self.do_wait_suspend(thread, frame, 'exception', arg, is_unhandled_exception=True)
|
||||
self.do_wait_suspend(thread, frame, 'exception', arg, EXCEPTION_TYPE_UNHANDLED)
|
||||
except:
|
||||
pydev_log.exception("We've got an error while stopping in unhandled exception: %s.", arg[0])
|
||||
finally:
|
||||
|
|
@ -2812,7 +2811,7 @@ class Dispatcher(object):
|
|||
def close(self):
|
||||
try:
|
||||
self.reader.do_kill_pydev_thread()
|
||||
except :
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -81,6 +81,8 @@ CMD_THREAD_RESUME_SINGLE_NOTIFICATION = 158
|
|||
CMD_STEP_OVER_MY_CODE = 159
|
||||
CMD_STEP_RETURN_MY_CODE = 160
|
||||
|
||||
CMD_SET_PY_EXCEPTION = 161
|
||||
|
||||
CMD_REDIRECT_OUTPUT = 200
|
||||
CMD_GET_NEXT_STATEMENT_TARGETS = 201
|
||||
CMD_SET_PROJECT_ROOTS = 202
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import pytest
|
|||
|
||||
|
||||
def some_call():
|
||||
assert 0
|
||||
assert 0 # raise here
|
||||
|
||||
|
||||
def test_example():
|
||||
|
|
|
|||
|
|
@ -314,7 +314,7 @@ def test_relative_paths(tmpdir):
|
|||
pydevd_file_utils.NORM_PATHS_CONTAINER.clear()
|
||||
abs_path = pydevd_file_utils.get_abs_path_real_path_and_base_from_file('my_dir/my_file.pyx')[0]
|
||||
assert 'site-packages' not in abs_path
|
||||
assert os.path.normcase(str(tmpdir)) in abs_path
|
||||
assert str(tmpdir) in abs_path
|
||||
assert pydevd_file_utils.exists('my_dir/my_file.pyx')
|
||||
finally:
|
||||
sys.path.remove(str(tmpdir))
|
||||
|
|
@ -344,7 +344,7 @@ def test_zip_paths(tmpdir):
|
|||
assert pydevd_file_utils.exists(zipfile_path)
|
||||
abspath, realpath, basename = pydevd_file_utils.get_abs_path_real_path_and_base_from_file(zipfile_path)
|
||||
if IS_WINDOWS:
|
||||
assert abspath == zipfile_path.lower()
|
||||
assert abspath == zipfile_path
|
||||
assert basename == zip_basename.lower()
|
||||
else:
|
||||
assert abspath == zipfile_path
|
||||
|
|
|
|||
|
|
@ -641,6 +641,26 @@ def test_case_handled_exception_breaks(case_setup):
|
|||
writer.finished_ok = True
|
||||
|
||||
|
||||
def _check_current_line(json_hit, current_line):
|
||||
if not isinstance(current_line, (list, tuple)):
|
||||
current_line = (current_line,)
|
||||
for frame in json_hit.stack_trace_response.body.stackFrames:
|
||||
if '(Current frame)' in frame['name']:
|
||||
if frame['line'] not in current_line:
|
||||
rep = json.dumps(json_hit.stack_trace_response.body.stackFrames, indent=4)
|
||||
raise AssertionError('Expected: %s to be one of: %s\nFrames:\n%s.' % (
|
||||
frame['line'],
|
||||
current_line,
|
||||
rep
|
||||
))
|
||||
|
||||
break
|
||||
else:
|
||||
rep = json.dumps(json_hit.stack_trace_response.body.stackFrames, indent=4)
|
||||
raise AssertionError('Could not find (Current frame) in any frame name in: %s.' % (
|
||||
rep))
|
||||
|
||||
|
||||
@pytest.mark.skipif(IS_PY26, reason='Not ok on Python 2.6')
|
||||
@pytest.mark.parametrize('stop', [False, True])
|
||||
def test_case_user_unhandled_exception(case_setup, stop):
|
||||
|
|
@ -665,8 +685,9 @@ def test_case_user_unhandled_exception(case_setup, stop):
|
|||
json_facade.write_make_initial_run()
|
||||
|
||||
if stop:
|
||||
json_facade.wait_for_thread_stopped(
|
||||
reason='exception', line=writer.get_line_index_with_content('stop here'), file=target)
|
||||
json_hit = json_facade.wait_for_thread_stopped(
|
||||
reason='exception', line=writer.get_line_index_with_content('raise here'), file=target)
|
||||
_check_current_line(json_hit, writer.get_line_index_with_content('stop here'))
|
||||
|
||||
json_facade.write_continue()
|
||||
|
||||
|
|
@ -705,21 +726,30 @@ def test_case_user_unhandled_exception_coroutine(case_setup, stop):
|
|||
json_facade.write_make_initial_run()
|
||||
|
||||
if stop:
|
||||
json_facade.wait_for_thread_stopped(
|
||||
reason='exception', line=writer.get_line_index_with_content('stop here 1'), file=basename)
|
||||
stop_line = writer.get_line_index_with_content('stop here 1')
|
||||
current_line = stop_line
|
||||
|
||||
json_hit = json_facade.wait_for_thread_stopped(
|
||||
reason='exception', line=stop_line, file=basename)
|
||||
_check_current_line(json_hit, current_line)
|
||||
|
||||
json_facade.write_continue()
|
||||
|
||||
json_facade.wait_for_thread_stopped(
|
||||
reason='exception', line=writer.get_line_index_with_content('stop here 2'), file=basename)
|
||||
current_line = writer.get_line_index_with_content('stop here 2')
|
||||
json_hit = json_facade.wait_for_thread_stopped(
|
||||
reason='exception', line=stop_line, file=basename)
|
||||
_check_current_line(json_hit, current_line)
|
||||
|
||||
json_facade.write_continue()
|
||||
|
||||
json_facade.wait_for_thread_stopped(
|
||||
reason='exception', line=(
|
||||
writer.get_line_index_with_content('stop here 3a'),
|
||||
writer.get_line_index_with_content('stop here 3b')
|
||||
), file=basename)
|
||||
current_line = (
|
||||
writer.get_line_index_with_content('stop here 3a'),
|
||||
writer.get_line_index_with_content('stop here 3b'),
|
||||
)
|
||||
|
||||
json_hit = json_facade.wait_for_thread_stopped(
|
||||
reason='exception', line=stop_line, file=basename)
|
||||
_check_current_line(json_hit, current_line)
|
||||
|
||||
json_facade.write_continue()
|
||||
|
||||
|
|
@ -734,7 +764,7 @@ def test_case_user_unhandled_exception_stop_on_yield(case_setup, pyfile):
|
|||
|
||||
def on_yield():
|
||||
yield
|
||||
raise AssertionError()
|
||||
raise AssertionError() # raise here
|
||||
|
||||
try:
|
||||
for _ in on_yield(): # stop here
|
||||
|
|
@ -765,8 +795,9 @@ def test_case_user_unhandled_exception_stop_on_yield(case_setup, pyfile):
|
|||
json_facade.write_set_exception_breakpoints(['userUnhandled'])
|
||||
json_facade.write_make_initial_run()
|
||||
|
||||
json_facade.wait_for_thread_stopped(
|
||||
reason='exception', line=writer.get_line_index_with_content('stop here'), file=case_error_on_yield)
|
||||
json_hit = json_facade.wait_for_thread_stopped(
|
||||
reason='exception', line=writer.get_line_index_with_content('raise here'), file=case_error_on_yield)
|
||||
_check_current_line(json_hit, writer.get_line_index_with_content('stop here'))
|
||||
|
||||
json_facade.write_continue()
|
||||
writer.finished_ok = True
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ class Client(components.Component):
|
|||
"exceptionBreakpointFilters": [
|
||||
{"filter": "raised", "label": "Raised Exceptions", "default": False},
|
||||
{"filter": "uncaught", "label": "Uncaught Exceptions", "default": True},
|
||||
# {"filter": "userUnhandled", "label": "User Uncaught Exceptions", "default": False},
|
||||
{"filter": "userUnhandled", "label": "User Uncaught Exceptions", "default": False},
|
||||
],
|
||||
}
|
||||
|
||||
|
|
@ -305,7 +305,7 @@ class Client(components.Component):
|
|||
python = request(python_key, json.array(unicode, vectorize=True, size=(0,)))
|
||||
if not len(python):
|
||||
python = [compat.filename(sys.executable)]
|
||||
|
||||
|
||||
python += request("pythonArgs", json.array(unicode, size=(0,)))
|
||||
request.arguments["pythonArgs"] = python[1:]
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue