diff --git a/src/ptvsd/_vendored/pydevd/_pydevd_bundle/_debug_adapter/__main__pydevd_gen_debug_adapter_protocol.py b/src/ptvsd/_vendored/pydevd/_pydevd_bundle/_debug_adapter/__main__pydevd_gen_debug_adapter_protocol.py index 152b4c7d..55bc13b6 100644 --- a/src/ptvsd/_vendored/pydevd/_pydevd_bundle/_debug_adapter/__main__pydevd_gen_debug_adapter_protocol.py +++ b/src/ptvsd/_vendored/pydevd/_pydevd_bundle/_debug_adapter/__main__pydevd_gen_debug_adapter_protocol.py @@ -87,6 +87,17 @@ def load_schema_data(): return json_schema_data +def load_custom_schema_data(): + import os.path + import json + + json_file = os.path.join(os.path.dirname(__file__), 'debugProtocolCustom.json') + + with open(json_file, 'rb') as json_contents: + json_schema_data = json.loads(json_contents.read()) + return json_schema_data + + def create_classes_to_generate_structure(json_schema_data): definitions = json_schema_data['definitions'] @@ -490,6 +501,7 @@ def gen_debugger_protocol(): raise AssertionError('Must be run with Python 3.6 onwards (to keep dict order).') classes_to_generate = create_classes_to_generate_structure(load_schema_data()) + classes_to_generate.update(create_classes_to_generate_structure(load_custom_schema_data())) class_to_generate = fill_properties_and_required_from_base(classes_to_generate) diff --git a/src/ptvsd/_vendored/pydevd/_pydevd_bundle/_debug_adapter/debugProtocolCustom.json b/src/ptvsd/_vendored/pydevd/_pydevd_bundle/_debug_adapter/debugProtocolCustom.json new file mode 100644 index 00000000..85311925 --- /dev/null +++ b/src/ptvsd/_vendored/pydevd/_pydevd_bundle/_debug_adapter/debugProtocolCustom.json @@ -0,0 +1,39 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Custom Debug Adapter Protocol", + "description": "Extension to the DAP to support additional features.", + "type": "object", + + + "definitions": { + + "SetDebuggerPropertyRequest": { + "allOf": [ { "$ref": "#/definitions/Request" }, { + "type": "object", + "description": "The request can be used to enable or disable debugger features.", + "properties": { + "command": { + "type": "string", + "enum": [ "setDebuggerProperty" ] + }, + "arguments": { + "$ref": "#/definitions/SetDebuggerPropertyArguments" + } + }, + "required": [ "command", "arguments" ] + }] + }, + "SetDebuggerPropertyArguments": { + "type": "object", + "description": "Arguments for 'setDebuggerProperty' request.", + "properties": { } + }, + "SetDebuggerPropertyResponse": { + "allOf": [ { "$ref": "#/definitions/Response" }, { + "type": "object", + "description": "Response to 'setDebuggerProperty' request. This is just an acknowledgement, so no body field is required." + }] + } + + } +} \ No newline at end of file diff --git a/src/ptvsd/_vendored/pydevd/_pydevd_bundle/_debug_adapter/pydevd_schema.py b/src/ptvsd/_vendored/pydevd/_pydevd_bundle/_debug_adapter/pydevd_schema.py index 64d6fe16..fad2ce1b 100644 --- a/src/ptvsd/_vendored/pydevd/_pydevd_bundle/_debug_adapter/pydevd_schema.py +++ b/src/ptvsd/_vendored/pydevd/_pydevd_bundle/_debug_adapter/pydevd_schema.py @@ -11478,6 +11478,197 @@ class ExceptionDetails(BaseSchema): return dct +@register_request('setDebuggerProperty') +@register +class SetDebuggerPropertyRequest(BaseSchema): + """ + The request can be used to enable or disable debugger features. + + Note: automatically generated code. Do not edit manually. + """ + + __props__ = { + "seq": { + "type": "integer", + "description": "Sequence number." + }, + "type": { + "type": "string", + "enum": [ + "request" + ] + }, + "command": { + "type": "string", + "enum": [ + "setDebuggerProperty" + ] + }, + "arguments": { + "type": "SetDebuggerPropertyArguments" + } + } + __refs__ = set(['arguments']) + + __slots__ = list(__props__.keys()) + ['kwargs'] + + def __init__(self, arguments, seq=-1, update_ids_from_dap=False, **kwargs): # noqa (update_ids_from_dap may be unused) + """ + :param string type: + :param string command: + :param SetDebuggerPropertyArguments arguments: + :param integer seq: Sequence number. + """ + self.type = 'request' + self.command = 'setDebuggerProperty' + if arguments is None: + self.arguments = SetDebuggerPropertyArguments() + else: + self.arguments = SetDebuggerPropertyArguments(update_ids_from_dap=update_ids_from_dap, **arguments) if arguments.__class__ != SetDebuggerPropertyArguments else arguments + self.seq = seq + self.kwargs = kwargs + + + def to_dict(self, update_ids_to_dap=False): # noqa (update_ids_to_dap may be unused) + type = self.type # noqa (assign to builtin) + command = self.command + arguments = self.arguments + seq = self.seq + dct = { + 'type': type, + 'command': command, + 'arguments': arguments.to_dict(update_ids_to_dap=update_ids_to_dap), + 'seq': seq, + } + dct.update(self.kwargs) + return dct + + +@register +class SetDebuggerPropertyArguments(BaseSchema): + """ + Arguments for 'setDebuggerProperty' request. + + Note: automatically generated code. Do not edit manually. + """ + + __props__ = {} + __refs__ = set() + + __slots__ = list(__props__.keys()) + ['kwargs'] + + def __init__(self, update_ids_from_dap=False, **kwargs): # noqa (update_ids_from_dap may be unused) + """ + + """ + + self.kwargs = kwargs + + + def to_dict(self, update_ids_to_dap=False): # noqa (update_ids_to_dap may be unused) + dct = { + } + dct.update(self.kwargs) + return dct + + +@register_response('setDebuggerProperty') +@register +class SetDebuggerPropertyResponse(BaseSchema): + """ + Response to 'setDebuggerProperty' request. This is just an acknowledgement, so no body field is + required. + + Note: automatically generated code. Do not edit manually. + """ + + __props__ = { + "seq": { + "type": "integer", + "description": "Sequence number." + }, + "type": { + "type": "string", + "enum": [ + "response" + ] + }, + "request_seq": { + "type": "integer", + "description": "Sequence number of the corresponding request." + }, + "success": { + "type": "boolean", + "description": "Outcome of the request." + }, + "command": { + "type": "string", + "description": "The command requested." + }, + "message": { + "type": "string", + "description": "Contains error message if success == false." + }, + "body": { + "type": [ + "array", + "boolean", + "integer", + "null", + "number", + "object", + "string" + ], + "description": "Contains request result if success is true and optional error details if success is false." + } + } + __refs__ = set() + + __slots__ = list(__props__.keys()) + ['kwargs'] + + def __init__(self, request_seq, success, command, seq=-1, message=None, body=None, update_ids_from_dap=False, **kwargs): # noqa (update_ids_from_dap may be unused) + """ + :param string type: + :param integer request_seq: Sequence number of the corresponding request. + :param boolean success: Outcome of the request. + :param string command: The command requested. + :param integer seq: Sequence number. + :param string message: Contains error message if success == false. + :param ['array', 'boolean', 'integer', 'null', 'number', 'object', 'string'] body: Contains request result if success is true and optional error details if success is false. + """ + self.type = 'response' + self.request_seq = request_seq + self.success = success + self.command = command + self.seq = seq + self.message = message + self.body = body + self.kwargs = kwargs + + + def to_dict(self, update_ids_to_dap=False): # noqa (update_ids_to_dap may be unused) + type = self.type # noqa (assign to builtin) + request_seq = self.request_seq + success = self.success + command = self.command + seq = self.seq + message = self.message + body = self.body + dct = { + 'type': type, + 'request_seq': request_seq, + 'success': success, + 'command': command, + 'seq': seq, + } + if message is not None: + dct['message'] = message + if body is not None: + dct['body'] = body + dct.update(self.kwargs) + return dct + + @register class ErrorResponseBody(BaseSchema): """ diff --git a/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_process_net_command_json.py b/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_process_net_command_json.py index 098dc301..c8bd08d6 100644 --- a/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_process_net_command_json.py +++ b/src/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_process_net_command_json.py @@ -9,6 +9,7 @@ from _pydevd_bundle._debug_adapter.pydevd_schema import (SourceBreakpoint, Scope VariablesResponseBody, SetVariableResponseBody, ModulesResponseBody, SourceResponseBody, GotoTargetsResponseBody, ExceptionOptions) from _pydevd_bundle.pydevd_api import PyDevdAPI +from _pydevd_bundle.pydevd_comm import pydevd_log from _pydevd_bundle.pydevd_comm_constants import ( CMD_RETURN, CMD_STEP_OVER_MY_CODE, CMD_STEP_OVER, CMD_STEP_INTO_MY_CODE, CMD_STEP_INTO, CMD_STEP_RETURN_MY_CODE, CMD_STEP_RETURN, CMD_SET_NEXT_STATEMENT) @@ -695,5 +696,49 @@ class _PyDevJsonCommandProcessor(object): response = pydevd_base_schema.build_response(request, kwargs={'body': {}}) return NetCommand(CMD_RETURN, 0, response, is_json=True) + def _can_set_dont_trace_pattern(self, py_db, start_patterns, end_patterns): + if py_db.is_cache_file_type_empty(): + return True + + if py_db.dont_trace_external_files.__name__ == 'dont_trace_files_property_request': + return py_db.dont_trace_external_files.start_patterns == start_patterns and \ + py_db.dont_trace_external_files.end_patterns == end_patterns + + return False + + def on_setdebuggerproperty_request(self, py_db, request): + args = request.arguments.kwargs + if 'dontTraceStartPatterns' in args and 'dontTraceEndPatterns' in args: + start_patterns = tuple(args['dontTraceStartPatterns']) + end_patterns = tuple(args['dontTraceEndPatterns']) + if self._can_set_dont_trace_pattern(py_db, start_patterns, end_patterns): + def dont_trace_files_property_request(abs_path): + result = abs_path.startswith(start_patterns) or \ + abs_path.endswith(end_patterns) + return result + dont_trace_files_property_request.start_patterns = start_patterns + dont_trace_files_property_request.end_patterns = end_patterns + py_db.dont_trace_external_files = dont_trace_files_property_request + else: + # Don't trace pattern cannot be changed after it is set once. There are caches + # throughout the debugger which rely on always having the same file type. + message = ("Calls to set or change don't trace patterns (via setDebuggerProperty) are not " + "allowed since debugging has already started or don't trace patterns are already set.") + pydevd_log(0, message) + response_args = {'success':False, 'body': {}, 'message': message} + response = pydevd_base_schema.build_response(request, kwargs=response_args) + return NetCommand(CMD_RETURN, 0, response, is_json=True) + + # TODO: Support other common settings. Note that not all of these might be relevant to python. + # JustMyCodeStepping: 0 or 1 + # AllowOutOfProcessSymbols: 0 or 1 + # DisableJITOptimization: 0 or 1 + # InterpreterOptions: 0 or 1 + # StopOnExceptionCrossingManagedBoundary: 0 or 1 + # WarnIfNoUserCodeOnLaunch: 0 or 1 + # EnableStepFiltering: true of false + + response = pydevd_base_schema.build_response(request, kwargs={'body': {}}) + return NetCommand(CMD_RETURN, 0, response, is_json=True) process_net_command_json = _PyDevJsonCommandProcessor(pydevd_base_schema.from_json).process_net_command_json diff --git a/src/ptvsd/_vendored/pydevd/pydevd.py b/src/ptvsd/_vendored/pydevd/pydevd.py index e35a1529..e4f2db3e 100644 --- a/src/ptvsd/_vendored/pydevd/pydevd.py +++ b/src/ptvsd/_vendored/pydevd/pydevd.py @@ -137,6 +137,7 @@ forked = False file_system_encoding = getfilesystemencoding() +_CACHE_FILE_TYPE = {} #======================================================================================================================= # PyDBCommandThread @@ -546,7 +547,31 @@ class PyDB(object): if val is not None: info.pydev_message = str(val) - def get_file_type(self, abs_real_path_and_basename): + def _internal_get_file_type(self, abs_real_path_and_basename): + basename = abs_real_path_and_basename[-1] + if basename.startswith(' 0 self.debug_options['DEBUG_STDLIB'] = not jmc + # TODO: Replace the line below with _forward_request_to_pydevd + # after fixing https://github.com/Microsoft/ptvsd/issues/1355 self.send_response(request) # PyDevd protocol event handlers diff --git a/tests/func/test_exception.py b/tests/func/test_exception.py index 82cf7b4d..2c5f11a7 100644 --- a/tests/func/test_exception.py +++ b/tests/func/test_exception.py @@ -361,12 +361,12 @@ def test_exception_stack(pyfile, run_as, start_method, max_frames): if max_frames == 'all': # trace back compresses repeated text min_expected_lines = 100 - max_expected_lines = 220 + max_expected_lines = 221 args = {'maxExceptionStackFrames': 0} elif max_frames == 'default': # default is all frames min_expected_lines = 100 - max_expected_lines = 220 + max_expected_lines = 221 args = {} else: min_expected_lines = 10 diff --git a/tests/test_internals_filter.py b/tests/test_internals_filter.py index 43f30e48..f5aebd8c 100644 --- a/tests/test_internals_filter.py +++ b/tests/test_internals_filter.py @@ -7,7 +7,6 @@ import pytest import ptvsd from ptvsd.wrapper import InternalsFilter -from ptvsd.wrapper import dont_trace_ptvsd_files INTERNAL_DIR = os.path.dirname(os.path.abspath(ptvsd.__file__)) @pytest.mark.parametrize('path', [ @@ -28,18 +27,3 @@ def test_internal_paths(path): def test_user_file_paths(path): int_filter = InternalsFilter() assert not int_filter.is_internal_path(path) - -@pytest.mark.parametrize('path, val', [ - (os.path.join(INTERNAL_DIR, 'wrapper.py'), True), - (os.path.join(INTERNAL_DIR, 'abcd', 'ptvsd', 'wrapper.py'), True), - (os.path.join(INTERNAL_DIR, 'ptvsd', 'wrapper.py'), True), - (os.path.join(INTERNAL_DIR, 'abcd', 'wrapper.py'), True), - (os.path.join('usr', 'abcd', 'ptvsd', 'wrapper.py'), False), - (os.path.join('C:', 'ptvsd', 'wrapper1.py'), False), - (os.path.join('C:', 'abcd', 'ptvsd', 'ptvsd.py'), False), - (os.path.join('usr', 'ptvsd', 'w.py'), False), - (os.path.join('ptvsd', 'w.py'), False), - (os.path.join('usr', 'abcd', 'ptvsd', 'tangle.py'), False), -]) -def test_ptvsd_paths(path, val): - assert val == dont_trace_ptvsd_files(os.path.normcase(path))