Skip to content
This repository has been archived by the owner on Aug 2, 2023. It is now read-only.

Commit

Permalink
Port SetExceptionBreakpoints DAP message to pydevd. Fixes #1287
Browse files Browse the repository at this point in the history
  • Loading branch information
fabioz committed Apr 4, 2019
1 parent aa4beb0 commit ed8f980
Show file tree
Hide file tree
Showing 13 changed files with 393 additions and 309 deletions.
2 changes: 1 addition & 1 deletion src/ptvsd/_vendored/pydevd/.travis_install_python_deps.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ fi
if [ "$PYDEVD_PYTHON_VERSION" = "3.7" ]; then
conda install --yes pyqt=5 matplotlib
# Note: track the latest django
pip install "django"
pip install "django>=2.1,<2.2"
fi

pip install untangle
Expand Down
9 changes: 9 additions & 0 deletions src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,15 @@ def remove_plugins_exception_breakpoint(self, py_db, exception_type, exception):

py_db.on_breakpoints_changed(removed=True)

def remove_all_exception_breakpoints(self, py_db):
py_db.break_on_uncaught_exceptions = {}
py_db.break_on_caught_exceptions = {}

plugin = py_db.plugin
if plugin is not None:
plugin.remove_all_exception_breakpoints(py_db)
py_db.on_breakpoints_changed(removed=True)

def set_project_roots(self, py_db, project_roots):
'''
:param unicode project_roots:
Expand Down
10 changes: 6 additions & 4 deletions src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py
Original file line number Diff line number Diff line change
Expand Up @@ -1135,17 +1135,19 @@ def internal_get_exception_details_json(dbg, request, thread_id, set_additional_
# This is an extra bit of data used by Visual Studio
source_path = frames[0][0] if frames else ''

# TODO: breakMode is set to always. This should be retrieved from exception
# breakpoint settings for that exception, or its parent chain. Currently json
# support for setExceptionBreakpoint is not implemented.
if thread.stop_reason == CMD_STEP_CAUGHT_EXCEPTION:
break_mode = pydevd_schema.ExceptionBreakMode.ALWAYS
else:
break_mode = pydevd_schema.ExceptionBreakMode.UNHANDLED

response = pydevd_schema.ExceptionInfoResponse(
request_seq=request.seq,
success=True,
command='exceptionInfo',
body=pydevd_schema.ExceptionInfoResponseBody(
exceptionId=name,
description=description,
breakMode=pydevd_schema.ExceptionBreakMode.ALWAYS,
breakMode=break_mode,
details=pydevd_schema.ExceptionDetails(
message=description,
typeName=name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from _pydevd_bundle._debug_adapter import pydevd_base_schema
from _pydevd_bundle._debug_adapter.pydevd_schema import (SourceBreakpoint, ScopesResponseBody, Scope,
VariablesResponseBody, SetVariableResponseBody, ModulesResponseBody, SourceResponseBody,
GotoTargetsResponseBody)
GotoTargetsResponseBody, ExceptionOptions)
from _pydevd_bundle.pydevd_api import PyDevdAPI
from _pydevd_bundle.pydevd_comm_constants import (
CMD_RETURN, CMD_STEP_OVER_MY_CODE, CMD_STEP_OVER, CMD_STEP_INTO_MY_CODE,
Expand Down Expand Up @@ -326,6 +326,7 @@ def on_disconnect_request(self, py_db, request):
:param DisconnectRequest request:
'''
self.api.remove_all_breakpoints(py_db, filename='*')
self.api.remove_all_exception_breakpoints(py_db)
self.api.request_resume_thread(thread_id='*')

response = pydevd_base_schema.build_response(request)
Expand Down Expand Up @@ -380,6 +381,97 @@ def on_setbreakpoints_request(self, py_db, request):
set_breakpoints_response = pydevd_base_schema.build_response(request, kwargs={'body':body})
return NetCommand(CMD_RETURN, 0, set_breakpoints_response, is_json=True)

def on_setexceptionbreakpoints_request(self, py_db, request):
'''
:param SetExceptionBreakpointsRequest request:
'''
# : :type arguments: SetExceptionBreakpointsArguments
arguments = request.arguments
filters = arguments.filters
exception_options = arguments.exceptionOptions
self.api.remove_all_exception_breakpoints(py_db)

# Can't set these in the DAP.
condition = None
expression = None
notify_on_first_raise_only = False

ignore_libraries = 1 if py_db.get_use_libraries_filter() else 0

if exception_options:
break_raised = True
break_uncaught = True

for option in exception_options:
option = ExceptionOptions(**option)
if not option.path:
continue

notify_on_handled_exceptions = 1 if option.breakMode == 'always' else 0
notify_on_unhandled_exceptions = 1 if option.breakMode in ('unhandled', 'userUnhandled') else 0
exception_paths = option.path

exception_names = []
if len(exception_paths) == 0:
continue

elif len(exception_paths) == 1:
if 'Python Exceptions' in exception_paths[0]['names']:
exception_names = ['BaseException']

else:
path_iterator = iter(exception_paths)
if 'Python Exceptions' in next(path_iterator)['names']:
for path in path_iterator:
for ex_name in path['names']:
exception_names.append(ex_name)

for exception_name in exception_names:
self.api.add_python_exception_breakpoint(
py_db,
exception_name,
condition,
expression,
notify_on_handled_exceptions,
notify_on_unhandled_exceptions,
notify_on_first_raise_only,
ignore_libraries
)

else:
break_raised = 'raised' in filters
break_uncaught = 'uncaught' in filters
if break_raised or break_uncaught:
notify_on_handled_exceptions = 1 if break_raised else 0
notify_on_unhandled_exceptions = 1 if break_uncaught else 0
exception = 'BaseException'

self.api.add_python_exception_breakpoint(
py_db,
exception,
condition,
expression,
notify_on_handled_exceptions,
notify_on_unhandled_exceptions,
notify_on_first_raise_only,
ignore_libraries
)

if break_raised or break_uncaught:
btype = None
if self._debug_options.get('DJANGO_DEBUG', False):
btype = 'django'
elif self._debug_options.get('FLASK_DEBUG', False):
btype = 'jinja2'

if btype:
self.api.add_plugins_exception_breakpoint(
py_db, btype, 'BaseException') # Note: Exception name could be anything here.

# Note: no body required on success.
set_breakpoints_response = pydevd_base_schema.build_response(request)
return NetCommand(CMD_RETURN, 0, set_breakpoints_response, is_json=True)

def on_stacktrace_request(self, py_db, request):
'''
:param StackTraceRequest request:
Expand Down
4 changes: 4 additions & 0 deletions src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_trace_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ def remove_exception_breakpoint(plugin, pydb, type, exception):
return False


def remove_all_exception_breakpoints(plugin, pydb):
return False


def get_breakpoints(plugin, pydb):
return None

Expand Down
7 changes: 7 additions & 0 deletions src/ptvsd/_vendored/pydevd/pydevd_plugins/django_debug.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@ def remove_exception_breakpoint(plugin, pydb, type, exception):
return False


def remove_all_exception_breakpoints(plugin, pydb):
if hasattr(pydb, 'django_exception_break'):
pydb.django_exception_break = {}
return True
return False


def get_breakpoints(plugin, pydb, type):
if type == 'django-line':
return pydb.django_breakpoints
Expand Down
7 changes: 7 additions & 0 deletions src/ptvsd/_vendored/pydevd/pydevd_plugins/jinja2_debug.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ def _init_plugin_breaks(pydb):
pydb.jinja2_breakpoints = {}


def remove_all_exception_breakpoints(plugin, pydb):
if hasattr(pydb, 'jinja2_exception_break'):
pydb.jinja2_exception_break = {}
return True
return False


def remove_exception_breakpoint(plugin, pydb, type, exception):
if type == 'jinja2':
try:
Expand Down
3 changes: 1 addition & 2 deletions src/ptvsd/_vendored/pydevd/tests_python/debugger_unittest.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,7 @@ def get_next_message(self, context_message, timeout=None):
frame = sys._getframe().f_back.f_back
frame_info = ''
while frame:
if frame.f_code.co_name in (
'wait_for_message', 'accept_json_message', 'wait_for_json_message', 'wait_for_response'):
if not frame.f_code.co_name.startswith('test_'):
frame = frame.f_back
continue

Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
import sys


def method3():
raise IndexError('foo')
raise IndexError('foo') # raise indexerror line


def method2():
return method3()

return method3() # reraise on method2


def method1():
try:
method2()
method2() # handle on method1
except:
pass # Ok, handled
assert '__exception__' not in sys._getframe().f_locals



if __name__ == '__main__':
method1()
print('TEST SUCEEDED!')
print('TEST SUCEEDED!')
21 changes: 15 additions & 6 deletions src/ptvsd/_vendored/pydevd/tests_python/test_debugger.py
Original file line number Diff line number Diff line change
Expand Up @@ -1607,7 +1607,11 @@ def test_case_handled_exceptions0(case_setup):
)
writer.write_make_initial_run()

hit = writer.wait_for_breakpoint_hit(REASON_CAUGHT_EXCEPTION, line=3)
hit = writer.wait_for_breakpoint_hit(
REASON_CAUGHT_EXCEPTION,
line=writer.get_line_index_with_content('raise indexerror line')
)

writer.write_run_thread(hit.thread_id)

writer.finished_ok = True
Expand Down Expand Up @@ -1646,13 +1650,16 @@ def check(hit):
assert unquote(unquote(msg.thread.frame[0]['file'])).endswith('_debugger_case_exceptions.py')
writer.write_run_thread(hit.thread_id)

hit = writer.wait_for_breakpoint_hit(REASON_CAUGHT_EXCEPTION, line=3)
hit = writer.wait_for_breakpoint_hit(
REASON_CAUGHT_EXCEPTION, line=writer.get_line_index_with_content('raise indexerror line'))
check(hit)

hit = writer.wait_for_breakpoint_hit(REASON_CAUGHT_EXCEPTION, line=6)
hit = writer.wait_for_breakpoint_hit(
REASON_CAUGHT_EXCEPTION, line=writer.get_line_index_with_content('reraise on method2'))
check(hit)

hit = writer.wait_for_breakpoint_hit(REASON_CAUGHT_EXCEPTION, line=10)
hit = writer.wait_for_breakpoint_hit(
REASON_CAUGHT_EXCEPTION, line=writer.get_line_index_with_content('handle on method1'))
check(hit)

writer.finished_ok = True
Expand Down Expand Up @@ -1702,7 +1709,8 @@ def get_environ(self):
)

writer.write_make_initial_run()
hit = writer.wait_for_breakpoint_hit(REASON_CAUGHT_EXCEPTION, line=3)
hit = writer.wait_for_breakpoint_hit(
REASON_CAUGHT_EXCEPTION, line=writer.get_line_index_with_content('raise indexerror line'))
writer.write_run_thread(hit.thread_id)

writer.finished_ok = True
Expand All @@ -1729,7 +1737,8 @@ def get_environ(self):
)

writer.write_make_initial_run()
hit = writer.wait_for_breakpoint_hit(REASON_CAUGHT_EXCEPTION, line=6)
hit = writer.wait_for_breakpoint_hit(
REASON_CAUGHT_EXCEPTION, line=writer.get_line_index_with_content('reraise on method2'))
writer.write_run_thread(hit.thread_id)

writer.finished_ok = True
Expand Down
Loading

0 comments on commit ed8f980

Please sign in to comment.