1import itertools 2import json 3import linecache 4import os 5import platform 6import sys 7from functools import partial 8 9import pydevd_file_utils 10from _pydev_bundle import pydev_log 11from _pydevd_bundle._debug_adapter import pydevd_base_schema, pydevd_schema 12from _pydevd_bundle._debug_adapter.pydevd_schema import ( 13 CompletionsResponseBody, EvaluateResponseBody, ExceptionOptions, 14 GotoTargetsResponseBody, ModulesResponseBody, ProcessEventBody, 15 ProcessEvent, Scope, ScopesResponseBody, SetExpressionResponseBody, 16 SetVariableResponseBody, SourceBreakpoint, SourceResponseBody, 17 VariablesResponseBody, SetBreakpointsResponseBody, Response, 18 Capabilities, PydevdAuthorizeRequest, Request, StepInTargetsResponse, StepInTarget, 19 StepInTargetsResponseBody, SetFunctionBreakpointsResponseBody) 20from _pydevd_bundle.pydevd_api import PyDevdAPI 21from _pydevd_bundle.pydevd_breakpoints import get_exception_class, FunctionBreakpoint 22from _pydevd_bundle.pydevd_comm_constants import ( 23 CMD_PROCESS_EVENT, CMD_RETURN, CMD_SET_NEXT_STATEMENT, CMD_STEP_INTO, 24 CMD_STEP_INTO_MY_CODE, CMD_STEP_OVER, CMD_STEP_OVER_MY_CODE, file_system_encoding, 25 CMD_STEP_RETURN_MY_CODE, CMD_STEP_RETURN) 26from _pydevd_bundle.pydevd_filtering import ExcludeFilter 27from _pydevd_bundle.pydevd_json_debug_options import _extract_debug_options, DebugOptions 28from _pydevd_bundle.pydevd_net_command import NetCommand 29from _pydevd_bundle.pydevd_utils import convert_dap_log_message_to_expression, ScopeRequest 30from _pydevd_bundle.pydevd_constants import (PY_IMPL_NAME, DebugInfoHolder, PY_VERSION_STR, 31 PY_IMPL_VERSION_STR, IS_64BIT_PROCESS, IS_PY2) 32from _pydevd_bundle.pydevd_trace_dispatch import USING_CYTHON 33from _pydevd_frame_eval.pydevd_frame_eval_main import USING_FRAME_EVAL 34from _pydevd_bundle.pydevd_comm import internal_get_step_in_targets_json 35from _pydevd_bundle.pydevd_additional_thread_info import set_additional_thread_info 36from _pydevd_bundle.pydevd_thread_lifecycle import pydevd_find_thread_by_id 37from _pydev_bundle._pydev_filesystem_encoding import getfilesystemencoding 38 39 40def _convert_rules_to_exclude_filters(rules, on_error): 41 exclude_filters = [] 42 if not isinstance(rules, list): 43 on_error('Invalid "rules" (expected list of dicts). Found: %s' % (rules,)) 44 45 else: 46 directory_exclude_filters = [] 47 module_exclude_filters = [] 48 glob_exclude_filters = [] 49 50 for rule in rules: 51 if not isinstance(rule, dict): 52 on_error('Invalid "rules" (expected list of dicts). Found: %s' % (rules,)) 53 continue 54 55 include = rule.get('include') 56 if include is None: 57 on_error('Invalid "rule" (expected dict with "include"). Found: %s' % (rule,)) 58 continue 59 60 path = rule.get('path') 61 module = rule.get('module') 62 if path is None and module is None: 63 on_error('Invalid "rule" (expected dict with "path" or "module"). Found: %s' % (rule,)) 64 continue 65 66 if path is not None: 67 glob_pattern = path 68 if '*' not in path and '?' not in path: 69 if os.path.isdir(glob_pattern): 70 # If a directory was specified, add a '/**' 71 # to be consistent with the glob pattern required 72 # by pydevd. 73 if not glob_pattern.endswith('/') and not glob_pattern.endswith('\\'): 74 glob_pattern += '/' 75 glob_pattern += '**' 76 directory_exclude_filters.append(ExcludeFilter(glob_pattern, not include, True)) 77 else: 78 glob_exclude_filters.append(ExcludeFilter(glob_pattern, not include, True)) 79 80 elif module is not None: 81 module_exclude_filters.append(ExcludeFilter(module, not include, False)) 82 83 else: 84 on_error('Internal error: expected path or module to be specified.') 85 86 # Note that we have to sort the directory/module exclude filters so that the biggest 87 # paths match first. 88 # i.e.: if we have: 89 # /sub1/sub2/sub3 90 # a rule with /sub1/sub2 would match before a rule only with /sub1. 91 directory_exclude_filters = sorted(directory_exclude_filters, key=lambda exclude_filter:-len(exclude_filter.name)) 92 module_exclude_filters = sorted(module_exclude_filters, key=lambda exclude_filter:-len(exclude_filter.name)) 93 exclude_filters = directory_exclude_filters + glob_exclude_filters + module_exclude_filters 94 95 return exclude_filters 96 97 98class IDMap(object): 99 100 def __init__(self): 101 self._value_to_key = {} 102 self._key_to_value = {} 103 self._next_id = partial(next, itertools.count(0)) 104 105 def obtain_value(self, key): 106 return self._key_to_value[key] 107 108 def obtain_key(self, value): 109 try: 110 key = self._value_to_key[value] 111 except KeyError: 112 key = self._next_id() 113 self._key_to_value[key] = value 114 self._value_to_key[value] = key 115 return key 116 117 118class PyDevJsonCommandProcessor(object): 119 120 def __init__(self, from_json): 121 self.from_json = from_json 122 self.api = PyDevdAPI() 123 self._options = DebugOptions() 124 self._next_breakpoint_id = partial(next, itertools.count(0)) 125 self._goto_targets_map = IDMap() 126 self._launch_or_attach_request_done = False 127 128 def process_net_command_json(self, py_db, json_contents, send_response=True): 129 ''' 130 Processes a debug adapter protocol json command. 131 ''' 132 133 DEBUG = False 134 135 try: 136 if isinstance(json_contents, bytes): 137 json_contents = json_contents.decode('utf-8') 138 139 request = self.from_json(json_contents, update_ids_from_dap=True) 140 except Exception as e: 141 try: 142 loaded_json = json.loads(json_contents) 143 request = Request(loaded_json.get('command', '<unknown>'), loaded_json['seq']) 144 except: 145 # There's not much we can do in this case... 146 pydev_log.exception('Error loading json: %s', json_contents) 147 return 148 149 error_msg = str(e) 150 if error_msg.startswith("'") and error_msg.endswith("'"): 151 error_msg = error_msg[1:-1] 152 153 # This means a failure processing the request (but we were able to load the seq, 154 # so, answer with a failure response). 155 def on_request(py_db, request): 156 error_response = { 157 'type': 'response', 158 'request_seq': request.seq, 159 'success': False, 160 'command': request.command, 161 'message': error_msg, 162 } 163 return NetCommand(CMD_RETURN, 0, error_response, is_json=True) 164 165 else: 166 if DebugInfoHolder.DEBUG_RECORD_SOCKET_READS and DebugInfoHolder.DEBUG_TRACE_LEVEL >= 1: 167 pydev_log.info('Process %s: %s\n' % ( 168 request.__class__.__name__, json.dumps(request.to_dict(update_ids_to_dap=True), indent=4, sort_keys=True),)) 169 170 assert request.type == 'request' 171 method_name = 'on_%s_request' % (request.command.lower(),) 172 on_request = getattr(self, method_name, None) 173 if on_request is None: 174 print('Unhandled: %s not available in PyDevJsonCommandProcessor.\n' % (method_name,)) 175 return 176 177 if DEBUG: 178 print('Handled in pydevd: %s (in PyDevJsonCommandProcessor).\n' % (method_name,)) 179 180 with py_db._main_lock: 181 if request.__class__ == PydevdAuthorizeRequest: 182 authorize_request = request # : :type authorize_request: PydevdAuthorizeRequest 183 access_token = authorize_request.arguments.debugServerAccessToken 184 py_db.authentication.login(access_token) 185 186 if not py_db.authentication.is_authenticated(): 187 response = Response( 188 request.seq, success=False, command=request.command, message='Client not authenticated.', body={}) 189 cmd = NetCommand(CMD_RETURN, 0, response, is_json=True) 190 py_db.writer.add_command(cmd) 191 return 192 193 cmd = on_request(py_db, request) 194 if cmd is not None and send_response: 195 py_db.writer.add_command(cmd) 196 197 def on_pydevdauthorize_request(self, py_db, request): 198 client_access_token = py_db.authentication.client_access_token 199 body = {'clientAccessToken': None} 200 if client_access_token: 201 body['clientAccessToken'] = client_access_token 202 203 response = pydevd_base_schema.build_response(request, kwargs={'body': body}) 204 return NetCommand(CMD_RETURN, 0, response, is_json=True) 205 206 def on_initialize_request(self, py_db, request): 207 body = Capabilities( 208 # Supported. 209 supportsConfigurationDoneRequest=True, 210 supportsConditionalBreakpoints=True, 211 supportsHitConditionalBreakpoints=True, 212 supportsEvaluateForHovers=True, 213 supportsSetVariable=True, 214 supportsGotoTargetsRequest=True, 215 supportsCompletionsRequest=True, 216 supportsModulesRequest=True, 217 supportsExceptionOptions=True, 218 supportsValueFormattingOptions=True, 219 supportsExceptionInfoRequest=True, 220 supportTerminateDebuggee=True, 221 supportsDelayedStackTraceLoading=True, 222 supportsLogPoints=True, 223 supportsSetExpression=True, 224 supportsTerminateRequest=True, 225 supportsClipboardContext=True, 226 supportsFunctionBreakpoints=True, 227 228 exceptionBreakpointFilters=[ 229 {'filter': 'raised', 'label': 'Raised Exceptions', 'default': False}, 230 {'filter': 'uncaught', 'label': 'Uncaught Exceptions', 'default': True}, 231 {"filter": "userUnhandled", "label": "User Uncaught Exceptions", "default": False}, 232 ], 233 234 # Not supported. 235 supportsStepBack=False, 236 supportsRestartFrame=False, 237 supportsStepInTargetsRequest=True, 238 supportsRestartRequest=False, 239 supportsLoadedSourcesRequest=False, 240 supportsTerminateThreadsRequest=False, 241 supportsDataBreakpoints=False, 242 supportsReadMemoryRequest=False, 243 supportsDisassembleRequest=False, 244 additionalModuleColumns=[], 245 completionTriggerCharacters=[], 246 supportedChecksumAlgorithms=[], 247 ).to_dict() 248 249 # Non-standard capabilities/info below. 250 body['supportsDebuggerProperties'] = True 251 252 body['pydevd'] = pydevd_info = {} 253 pydevd_info['processId'] = os.getpid() 254 self.api.notify_initialize(py_db) 255 response = pydevd_base_schema.build_response(request, kwargs={'body': body}) 256 return NetCommand(CMD_RETURN, 0, response, is_json=True) 257 258 def on_configurationdone_request(self, py_db, request): 259 ''' 260 :param ConfigurationDoneRequest request: 261 ''' 262 if not self._launch_or_attach_request_done: 263 pydev_log.critical('Missing launch request or attach request before configuration done request.') 264 265 self.api.run(py_db) 266 self.api.notify_configuration_done(py_db) 267 268 configuration_done_response = pydevd_base_schema.build_response(request) 269 return NetCommand(CMD_RETURN, 0, configuration_done_response, is_json=True) 270 271 def on_threads_request(self, py_db, request): 272 ''' 273 :param ThreadsRequest request: 274 ''' 275 return self.api.list_threads(py_db, request.seq) 276 277 def on_terminate_request(self, py_db, request): 278 ''' 279 :param TerminateRequest request: 280 ''' 281 self._request_terminate_process(py_db) 282 response = pydevd_base_schema.build_response(request) 283 return NetCommand(CMD_RETURN, 0, response, is_json=True) 284 285 def _request_terminate_process(self, py_db): 286 self.api.request_terminate_process(py_db) 287 288 def on_completions_request(self, py_db, request): 289 ''' 290 :param CompletionsRequest request: 291 ''' 292 arguments = request.arguments # : :type arguments: CompletionsArguments 293 seq = request.seq 294 text = arguments.text 295 frame_id = arguments.frameId 296 thread_id = py_db.suspended_frames_manager.get_thread_id_for_variable_reference( 297 frame_id) 298 299 if thread_id is None: 300 body = CompletionsResponseBody([]) 301 variables_response = pydevd_base_schema.build_response( 302 request, 303 kwargs={ 304 'body': body, 305 'success': False, 306 'message': 'Thread to get completions seems to have resumed already.' 307 }) 308 return NetCommand(CMD_RETURN, 0, variables_response, is_json=True) 309 310 # Note: line and column are 1-based (convert to 0-based for pydevd). 311 column = arguments.column - 1 312 313 if arguments.line is None: 314 # line is optional 315 line = -1 316 else: 317 line = arguments.line - 1 318 319 self.api.request_completions(py_db, seq, thread_id, frame_id, text, line=line, column=column) 320 321 def _resolve_remote_root(self, local_root, remote_root): 322 if remote_root == '.': 323 cwd = os.getcwd() 324 append_pathsep = local_root.endswith('\\') or local_root.endswith('/') 325 return cwd + (os.path.sep if append_pathsep else '') 326 return remote_root 327 328 def _set_debug_options(self, py_db, args, start_reason): 329 rules = args.get('rules') 330 stepping_resumes_all_threads = args.get('steppingResumesAllThreads', True) 331 self.api.set_stepping_resumes_all_threads(py_db, stepping_resumes_all_threads) 332 333 terminate_child_processes = args.get('terminateChildProcesses', True) 334 self.api.set_terminate_child_processes(py_db, terminate_child_processes) 335 336 variable_presentation = args.get('variablePresentation', None) 337 if isinstance(variable_presentation, dict): 338 339 def get_variable_presentation(setting, default): 340 value = variable_presentation.get(setting, default) 341 if value not in ('group', 'inline', 'hide'): 342 pydev_log.info( 343 'The value set for "%s" (%s) in the variablePresentation is not valid. Valid values are: "group", "inline", "hide"' % ( 344 setting, value,)) 345 value = default 346 347 return value 348 349 default = get_variable_presentation('all', 'group') 350 351 special_presentation = get_variable_presentation('special', default) 352 function_presentation = get_variable_presentation('function', default) 353 class_presentation = get_variable_presentation('class', default) 354 protected_presentation = get_variable_presentation('protected', default) 355 356 self.api.set_variable_presentation(py_db, self.api.VariablePresentation( 357 special_presentation, 358 function_presentation, 359 class_presentation, 360 protected_presentation 361 )) 362 363 exclude_filters = [] 364 365 if rules is not None: 366 exclude_filters = _convert_rules_to_exclude_filters( 367 rules, lambda msg: self.api.send_error_message(py_db, msg)) 368 369 self.api.set_exclude_filters(py_db, exclude_filters) 370 371 debug_options = _extract_debug_options( 372 args.get('options'), 373 args.get('debugOptions'), 374 ) 375 self._options.update_fom_debug_options(debug_options) 376 self._options.update_from_args(args) 377 378 self.api.set_use_libraries_filter(py_db, self._options.just_my_code) 379 380 path_mappings = [] 381 for pathMapping in args.get('pathMappings', []): 382 localRoot = pathMapping.get('localRoot', '') 383 remoteRoot = pathMapping.get('remoteRoot', '') 384 remoteRoot = self._resolve_remote_root(localRoot, remoteRoot) 385 if (localRoot != '') and (remoteRoot != ''): 386 path_mappings.append((localRoot, remoteRoot)) 387 388 if bool(path_mappings): 389 pydevd_file_utils.setup_client_server_paths(path_mappings) 390 391 if self._options.redirect_output: 392 py_db.enable_output_redirection(True, True) 393 else: 394 py_db.enable_output_redirection(False, False) 395 396 self.api.set_show_return_values(py_db, self._options.show_return_value) 397 398 if not self._options.break_system_exit_zero: 399 ignore_system_exit_codes = [0, None] 400 if self._options.django_debug or self._options.flask_debug: 401 ignore_system_exit_codes += [3] 402 403 self.api.set_ignore_system_exit_codes(py_db, ignore_system_exit_codes) 404 405 auto_reload = args.get('autoReload', {}) 406 if not isinstance(auto_reload, dict): 407 pydev_log.info('Expected autoReload to be a dict. Received: %s' % (auto_reload,)) 408 auto_reload = {} 409 410 enable_auto_reload = auto_reload.get('enable', False) 411 watch_dirs = auto_reload.get('watchDirectories') 412 if not watch_dirs: 413 watch_dirs = [] 414 # Note: by default this is no longer done because on some cases there are entries in the PYTHONPATH 415 # such as the home directory or /python/x64, where the site packages are in /python/x64/libs, so, 416 # we only watch the current working directory as well as executed script. 417 # check = getattr(sys, 'path', [])[:] 418 # # By default only watch directories that are in the project roots / 419 # # program dir (if available), sys.argv[0], as well as the current dir (we don't want to 420 # # listen to the whole site-packages by default as it can be huge). 421 # watch_dirs = [pydevd_file_utils.absolute_path(w) for w in check] 422 # watch_dirs = [w for w in watch_dirs if py_db.in_project_roots_filename_uncached(w) and os.path.isdir(w)] 423 424 program = args.get('program') 425 if program: 426 if os.path.isdir(program): 427 watch_dirs.append(program) 428 else: 429 watch_dirs.append(os.path.dirname(program)) 430 watch_dirs.append(os.path.abspath('.')) 431 432 argv = getattr(sys, 'argv', []) 433 if argv: 434 f = argv[0] 435 if os.path.isdir(f): 436 watch_dirs.append(program) 437 else: 438 watch_dirs.append(os.path.dirname(f)) 439 440 if not isinstance(watch_dirs, (list, set, tuple)): 441 watch_dirs = (watch_dirs,) 442 new_watch_dirs = set() 443 for w in watch_dirs: 444 try: 445 if IS_PY2 and isinstance(w, unicode): 446 w = w.encode(getfilesystemencoding()) 447 448 new_watch_dirs.add(pydevd_file_utils.get_path_with_real_case(pydevd_file_utils.absolute_path(w))) 449 except Exception: 450 pydev_log.exception('Error adding watch dir: %s', w) 451 watch_dirs = new_watch_dirs 452 453 poll_target_time = auto_reload.get('pollingInterval', 1) 454 exclude_patterns = auto_reload.get('exclude', ('**/.git/**', '**/__pycache__/**', '**/node_modules/**', '**/.metadata/**', '**/site-packages/**')) 455 include_patterns = auto_reload.get('include', ('**/*.py', '**/*.pyw')) 456 self.api.setup_auto_reload_watcher( 457 py_db, enable_auto_reload, watch_dirs, poll_target_time, exclude_patterns, include_patterns) 458 459 if self._options.stop_on_entry and start_reason == 'launch': 460 self.api.stop_on_entry() 461 462 def _send_process_event(self, py_db, start_method): 463 argv = getattr(sys, 'argv', []) 464 if len(argv) > 0: 465 name = argv[0] 466 else: 467 name = '' 468 469 if isinstance(name, bytes): 470 name = name.decode(file_system_encoding, 'replace') 471 name = name.encode('utf-8') 472 473 body = ProcessEventBody( 474 name=name, 475 systemProcessId=os.getpid(), 476 isLocalProcess=True, 477 startMethod=start_method, 478 ) 479 event = ProcessEvent(body) 480 py_db.writer.add_command(NetCommand(CMD_PROCESS_EVENT, 0, event, is_json=True)) 481 482 def _handle_launch_or_attach_request(self, py_db, request, start_reason): 483 self._send_process_event(py_db, start_reason) 484 self._launch_or_attach_request_done = True 485 self.api.set_enable_thread_notifications(py_db, True) 486 self._set_debug_options(py_db, request.arguments.kwargs, start_reason=start_reason) 487 response = pydevd_base_schema.build_response(request) 488 return NetCommand(CMD_RETURN, 0, response, is_json=True) 489 490 def on_launch_request(self, py_db, request): 491 ''' 492 :param LaunchRequest request: 493 ''' 494 return self._handle_launch_or_attach_request(py_db, request, start_reason='launch') 495 496 def on_attach_request(self, py_db, request): 497 ''' 498 :param AttachRequest request: 499 ''' 500 return self._handle_launch_or_attach_request(py_db, request, start_reason='attach') 501 502 def on_pause_request(self, py_db, request): 503 ''' 504 :param PauseRequest request: 505 ''' 506 arguments = request.arguments # : :type arguments: PauseArguments 507 thread_id = arguments.threadId 508 509 self.api.request_suspend_thread(py_db, thread_id=thread_id) 510 511 response = pydevd_base_schema.build_response(request) 512 return NetCommand(CMD_RETURN, 0, response, is_json=True) 513 514 def on_continue_request(self, py_db, request): 515 ''' 516 :param ContinueRequest request: 517 ''' 518 arguments = request.arguments # : :type arguments: ContinueArguments 519 thread_id = arguments.threadId 520 521 def on_resumed(): 522 body = {'allThreadsContinued': thread_id == '*'} 523 response = pydevd_base_schema.build_response(request, kwargs={'body': body}) 524 cmd = NetCommand(CMD_RETURN, 0, response, is_json=True) 525 py_db.writer.add_command(cmd) 526 527 # Only send resumed notification when it has actually resumed! 528 # (otherwise the user could send a continue, receive the notification and then 529 # request a new pause which would be paused without sending any notification as 530 # it didn't really run in the first place). 531 py_db.threads_suspended_single_notification.add_on_resumed_callback(on_resumed) 532 self.api.request_resume_thread(thread_id) 533 534 def on_next_request(self, py_db, request): 535 ''' 536 :param NextRequest request: 537 ''' 538 arguments = request.arguments # : :type arguments: NextArguments 539 thread_id = arguments.threadId 540 541 if py_db.get_use_libraries_filter(): 542 step_cmd_id = CMD_STEP_OVER_MY_CODE 543 else: 544 step_cmd_id = CMD_STEP_OVER 545 546 self.api.request_step(py_db, thread_id, step_cmd_id) 547 548 response = pydevd_base_schema.build_response(request) 549 return NetCommand(CMD_RETURN, 0, response, is_json=True) 550 551 def on_stepin_request(self, py_db, request): 552 ''' 553 :param StepInRequest request: 554 ''' 555 arguments = request.arguments # : :type arguments: StepInArguments 556 thread_id = arguments.threadId 557 558 target_id = arguments.targetId 559 if target_id is not None: 560 thread = pydevd_find_thread_by_id(thread_id) 561 info = set_additional_thread_info(thread) 562 target_id_to_smart_step_into_variant = info.target_id_to_smart_step_into_variant 563 if not target_id_to_smart_step_into_variant: 564 variables_response = pydevd_base_schema.build_response( 565 request, 566 kwargs={ 567 'success': False, 568 'message': 'Unable to step into target (no targets are saved in the thread info).' 569 }) 570 return NetCommand(CMD_RETURN, 0, variables_response, is_json=True) 571 572 variant = target_id_to_smart_step_into_variant.get(target_id) 573 if variant is not None: 574 parent = variant.parent 575 if parent is not None: 576 self.api.request_smart_step_into(py_db, request.seq, thread_id, parent.offset, variant.offset) 577 else: 578 self.api.request_smart_step_into(py_db, request.seq, thread_id, variant.offset, -1) 579 else: 580 variables_response = pydevd_base_schema.build_response( 581 request, 582 kwargs={ 583 'success': False, 584 'message': 'Unable to find step into target %s. Available targets: %s' % ( 585 target_id, target_id_to_smart_step_into_variant) 586 }) 587 return NetCommand(CMD_RETURN, 0, variables_response, is_json=True) 588 589 else: 590 if py_db.get_use_libraries_filter(): 591 step_cmd_id = CMD_STEP_INTO_MY_CODE 592 else: 593 step_cmd_id = CMD_STEP_INTO 594 595 self.api.request_step(py_db, thread_id, step_cmd_id) 596 597 response = pydevd_base_schema.build_response(request) 598 return NetCommand(CMD_RETURN, 0, response, is_json=True) 599 600 def on_stepintargets_request(self, py_db, request): 601 ''' 602 :param StepInTargetsRequest request: 603 ''' 604 frame_id = request.arguments.frameId 605 thread_id = py_db.suspended_frames_manager.get_thread_id_for_variable_reference( 606 frame_id) 607 608 if thread_id is None: 609 body = StepInTargetsResponseBody([]) 610 variables_response = pydevd_base_schema.build_response( 611 request, 612 kwargs={ 613 'body': body, 614 'success': False, 615 'message': 'Unable to get thread_id from frame_id (thread to get step in targets seems to have resumed already).' 616 }) 617 return NetCommand(CMD_RETURN, 0, variables_response, is_json=True) 618 619 py_db.post_method_as_internal_command( 620 thread_id, internal_get_step_in_targets_json, request.seq, thread_id, frame_id, request, set_additional_thread_info) 621 622 def on_stepout_request(self, py_db, request): 623 ''' 624 :param StepOutRequest request: 625 ''' 626 arguments = request.arguments # : :type arguments: StepOutArguments 627 thread_id = arguments.threadId 628 629 if py_db.get_use_libraries_filter(): 630 step_cmd_id = CMD_STEP_RETURN_MY_CODE 631 else: 632 step_cmd_id = CMD_STEP_RETURN 633 634 self.api.request_step(py_db, thread_id, step_cmd_id) 635 636 response = pydevd_base_schema.build_response(request) 637 return NetCommand(CMD_RETURN, 0, response, is_json=True) 638 639 def _get_hit_condition_expression(self, hit_condition): 640 '''Following hit condition values are supported 641 642 * x or == x when breakpoint is hit x times 643 * >= x when breakpoint is hit more than or equal to x times 644 * % x when breakpoint is hit multiple of x times 645 646 Returns '@HIT@ == x' where @HIT@ will be replaced by number of hits 647 ''' 648 if not hit_condition: 649 return None 650 651 expr = hit_condition.strip() 652 try: 653 int(expr) 654 return '@HIT@ == {}'.format(expr) 655 except ValueError: 656 pass 657 658 if expr.startswith('%'): 659 return '@HIT@ {} == 0'.format(expr) 660 661 if expr.startswith('==') or \ 662 expr.startswith('>') or \ 663 expr.startswith('<'): 664 return '@HIT@ {}'.format(expr) 665 666 return hit_condition 667 668 def on_disconnect_request(self, py_db, request): 669 ''' 670 :param DisconnectRequest request: 671 ''' 672 if request.arguments.terminateDebuggee: 673 self._request_terminate_process(py_db) 674 response = pydevd_base_schema.build_response(request) 675 return NetCommand(CMD_RETURN, 0, response, is_json=True) 676 677 self._launch_or_attach_request_done = False 678 py_db.enable_output_redirection(False, False) 679 self.api.request_disconnect(py_db, resume_threads=True) 680 681 response = pydevd_base_schema.build_response(request) 682 return NetCommand(CMD_RETURN, 0, response, is_json=True) 683 684 def _verify_launch_or_attach_done(self, request): 685 if not self._launch_or_attach_request_done: 686 # Note that to validate the breakpoints we need the launch request to be done already 687 # (otherwise the filters wouldn't be set for the breakpoint validation). 688 if request.command == 'setFunctionBreakpoints': 689 body = SetFunctionBreakpointsResponseBody([]) 690 else: 691 body = SetBreakpointsResponseBody([]) 692 response = pydevd_base_schema.build_response( 693 request, 694 kwargs={ 695 'body': body, 696 'success': False, 697 'message': 'Breakpoints may only be set after the launch request is received.' 698 }) 699 return NetCommand(CMD_RETURN, 0, response, is_json=True) 700 701 def on_setfunctionbreakpoints_request(self, py_db, request): 702 ''' 703 :param SetFunctionBreakpointsRequest request: 704 ''' 705 response = self._verify_launch_or_attach_done(request) 706 if response is not None: 707 return response 708 709 arguments = request.arguments # : :type arguments: SetFunctionBreakpointsArguments 710 function_breakpoints = [] 711 suspend_policy = 'ALL' 712 713 # Not currently covered by the DAP. 714 is_logpoint = False 715 expression = None 716 717 breakpoints_set = [] 718 for bp in arguments.breakpoints: 719 hit_condition = self._get_hit_condition_expression(bp.get('hitCondition')) 720 condition = bp.get('condition') 721 722 function_breakpoints.append( 723 FunctionBreakpoint(bp['name'], condition, expression, suspend_policy, hit_condition, is_logpoint)) 724 725 # Note: always succeeds. 726 breakpoints_set.append(pydevd_schema.Breakpoint( 727 verified=True, id=self._next_breakpoint_id()).to_dict()) 728 729 self.api.set_function_breakpoints(py_db, function_breakpoints) 730 731 body = {'breakpoints': breakpoints_set} 732 set_breakpoints_response = pydevd_base_schema.build_response(request, kwargs={'body': body}) 733 return NetCommand(CMD_RETURN, 0, set_breakpoints_response, is_json=True) 734 735 def on_setbreakpoints_request(self, py_db, request): 736 ''' 737 :param SetBreakpointsRequest request: 738 ''' 739 response = self._verify_launch_or_attach_done(request) 740 if response is not None: 741 return response 742 743 arguments = request.arguments # : :type arguments: SetBreakpointsArguments 744 # TODO: Path is optional here it could be source reference. 745 filename = self.api.filename_to_str(arguments.source.path) 746 func_name = 'None' 747 748 self.api.remove_all_breakpoints(py_db, filename) 749 750 btype = 'python-line' 751 suspend_policy = 'ALL' 752 753 if not filename.lower().endswith('.py'): # Note: check based on original file, not mapping. 754 if self._options.django_debug: 755 btype = 'django-line' 756 elif self._options.flask_debug: 757 btype = 'jinja2-line' 758 759 breakpoints_set = [] 760 761 for source_breakpoint in arguments.breakpoints: 762 source_breakpoint = SourceBreakpoint(**source_breakpoint) 763 line = source_breakpoint.line 764 condition = source_breakpoint.condition 765 breakpoint_id = line 766 767 hit_condition = self._get_hit_condition_expression(source_breakpoint.hitCondition) 768 log_message = source_breakpoint.logMessage 769 if not log_message: 770 is_logpoint = None 771 expression = None 772 else: 773 is_logpoint = True 774 expression = convert_dap_log_message_to_expression(log_message) 775 776 result = self.api.add_breakpoint( 777 py_db, filename, btype, breakpoint_id, line, condition, func_name, expression, suspend_policy, hit_condition, is_logpoint, adjust_line=True) 778 error_code = result.error_code 779 780 if error_code: 781 if error_code == self.api.ADD_BREAKPOINT_FILE_NOT_FOUND: 782 error_msg = 'Breakpoint in file that does not exist.' 783 784 elif error_code == self.api.ADD_BREAKPOINT_FILE_EXCLUDED_BY_FILTERS: 785 error_msg = 'Breakpoint in file excluded by filters.' 786 if py_db.get_use_libraries_filter(): 787 error_msg += ('\nNote: may be excluded because of "justMyCode" option (default == true).' 788 'Try setting \"justMyCode\": false in the debug configuration (e.g., launch.json).\n') 789 790 else: 791 # Shouldn't get here. 792 error_msg = 'Breakpoint not validated (reason unknown -- please report as bug).' 793 794 breakpoints_set.append(pydevd_schema.Breakpoint( 795 verified=False, line=result.translated_line, message=error_msg, source=arguments.source).to_dict()) 796 else: 797 # Note that the id is made up (the id for pydevd is unique only within a file, so, the 798 # line is used for it). 799 # Also, the id is currently not used afterwards, so, we don't even keep a mapping. 800 breakpoints_set.append(pydevd_schema.Breakpoint( 801 verified=True, id=self._next_breakpoint_id(), line=result.translated_line, source=arguments.source).to_dict()) 802 803 body = {'breakpoints': breakpoints_set} 804 set_breakpoints_response = pydevd_base_schema.build_response(request, kwargs={'body': body}) 805 return NetCommand(CMD_RETURN, 0, set_breakpoints_response, is_json=True) 806 807 def on_setexceptionbreakpoints_request(self, py_db, request): 808 ''' 809 :param SetExceptionBreakpointsRequest request: 810 ''' 811 # : :type arguments: SetExceptionBreakpointsArguments 812 arguments = request.arguments 813 filters = arguments.filters 814 exception_options = arguments.exceptionOptions 815 self.api.remove_all_exception_breakpoints(py_db) 816 817 # Can't set these in the DAP. 818 condition = None 819 expression = None 820 notify_on_first_raise_only = False 821 822 ignore_libraries = 1 if py_db.get_use_libraries_filter() else 0 823 824 if exception_options: 825 break_raised = False 826 break_uncaught = False 827 828 for option in exception_options: 829 option = ExceptionOptions(**option) 830 if not option.path: 831 continue 832 833 # never: never breaks 834 # 835 # always: always breaks 836 # 837 # unhandled: breaks when exception unhandled 838 # 839 # userUnhandled: breaks if the exception is not handled by user code 840 841 notify_on_handled_exceptions = 1 if option.breakMode == 'always' else 0 842 notify_on_unhandled_exceptions = 1 if option.breakMode == 'unhandled' else 0 843 notify_on_user_unhandled_exceptions = 1 if option.breakMode == 'userUnhandled' else 0 844 exception_paths = option.path 845 break_raised |= notify_on_handled_exceptions 846 break_uncaught |= notify_on_unhandled_exceptions 847 848 exception_names = [] 849 if len(exception_paths) == 0: 850 continue 851 852 elif len(exception_paths) == 1: 853 if 'Python Exceptions' in exception_paths[0]['names']: 854 exception_names = ['BaseException'] 855 856 else: 857 path_iterator = iter(exception_paths) 858 if 'Python Exceptions' in next(path_iterator)['names']: 859 for path in path_iterator: 860 for ex_name in path['names']: 861 exception_names.append(ex_name) 862 863 for exception_name in exception_names: 864 self.api.add_python_exception_breakpoint( 865 py_db, 866 exception_name, 867 condition, 868 expression, 869 notify_on_handled_exceptions, 870 notify_on_unhandled_exceptions, 871 notify_on_user_unhandled_exceptions, 872 notify_on_first_raise_only, 873 ignore_libraries 874 ) 875 876 else: 877 break_raised = 'raised' in filters 878 break_uncaught = 'uncaught' in filters 879 break_user = 'userUnhandled' in filters 880 if break_raised or break_uncaught or break_user: 881 notify_on_handled_exceptions = 1 if break_raised else 0 882 notify_on_unhandled_exceptions = 1 if break_uncaught else 0 883 notify_on_user_unhandled_exceptions = 1 if break_user else 0 884 exception = 'BaseException' 885 886 self.api.add_python_exception_breakpoint( 887 py_db, 888 exception, 889 condition, 890 expression, 891 notify_on_handled_exceptions, 892 notify_on_unhandled_exceptions, 893 notify_on_user_unhandled_exceptions, 894 notify_on_first_raise_only, 895 ignore_libraries 896 ) 897 898 if break_raised: 899 btype = None 900 if self._options.django_debug: 901 btype = 'django' 902 elif self._options.flask_debug: 903 btype = 'jinja2' 904 905 if btype: 906 self.api.add_plugins_exception_breakpoint( 907 py_db, btype, 'BaseException') # Note: Exception name could be anything here. 908 909 # Note: no body required on success. 910 set_breakpoints_response = pydevd_base_schema.build_response(request) 911 return NetCommand(CMD_RETURN, 0, set_breakpoints_response, is_json=True) 912 913 def on_stacktrace_request(self, py_db, request): 914 ''' 915 :param StackTraceRequest request: 916 ''' 917 # : :type stack_trace_arguments: StackTraceArguments 918 stack_trace_arguments = request.arguments 919 thread_id = stack_trace_arguments.threadId 920 start_frame = stack_trace_arguments.startFrame 921 levels = stack_trace_arguments.levels 922 923 fmt = stack_trace_arguments.format 924 if hasattr(fmt, 'to_dict'): 925 fmt = fmt.to_dict() 926 self.api.request_stack(py_db, request.seq, thread_id, fmt=fmt, start_frame=start_frame, levels=levels) 927 928 def on_exceptioninfo_request(self, py_db, request): 929 ''' 930 :param ExceptionInfoRequest request: 931 ''' 932 # : :type exception_into_arguments: ExceptionInfoArguments 933 exception_into_arguments = request.arguments 934 thread_id = exception_into_arguments.threadId 935 max_frames = self._options.max_exception_stack_frames 936 self.api.request_exception_info_json(py_db, request, thread_id, max_frames) 937 938 def on_scopes_request(self, py_db, request): 939 ''' 940 Scopes are the top-level items which appear for a frame (so, we receive the frame id 941 and provide the scopes it has). 942 943 :param ScopesRequest request: 944 ''' 945 frame_id = request.arguments.frameId 946 947 variables_reference = frame_id 948 scopes = [ 949 Scope('Locals', ScopeRequest(int(variables_reference), 'locals'), False, presentationHint='locals'), 950 Scope('Globals', ScopeRequest(int(variables_reference), 'globals'), False), 951 ] 952 body = ScopesResponseBody(scopes) 953 scopes_response = pydevd_base_schema.build_response(request, kwargs={'body': body}) 954 return NetCommand(CMD_RETURN, 0, scopes_response, is_json=True) 955 956 def on_evaluate_request(self, py_db, request): 957 ''' 958 :param EvaluateRequest request: 959 ''' 960 # : :type arguments: EvaluateArguments 961 arguments = request.arguments 962 963 if arguments.frameId is None: 964 self.api.request_exec_or_evaluate_json(py_db, request, thread_id='*') 965 else: 966 thread_id = py_db.suspended_frames_manager.get_thread_id_for_variable_reference( 967 arguments.frameId) 968 969 if thread_id is not None: 970 self.api.request_exec_or_evaluate_json( 971 py_db, request, thread_id) 972 else: 973 body = EvaluateResponseBody('', 0) 974 response = pydevd_base_schema.build_response( 975 request, 976 kwargs={ 977 'body': body, 978 'success': False, 979 'message': 'Unable to find thread for evaluation.' 980 }) 981 return NetCommand(CMD_RETURN, 0, response, is_json=True) 982 983 def on_setexpression_request(self, py_db, request): 984 # : :type arguments: SetExpressionArguments 985 arguments = request.arguments 986 987 thread_id = py_db.suspended_frames_manager.get_thread_id_for_variable_reference( 988 arguments.frameId) 989 990 if thread_id is not None: 991 self.api.request_set_expression_json(py_db, request, thread_id) 992 else: 993 body = SetExpressionResponseBody('') 994 response = pydevd_base_schema.build_response( 995 request, 996 kwargs={ 997 'body': body, 998 'success': False, 999 'message': 'Unable to find thread to set expression.' 1000 }) 1001 return NetCommand(CMD_RETURN, 0, response, is_json=True) 1002 1003 def on_variables_request(self, py_db, request): 1004 ''' 1005 Variables can be asked whenever some place returned a variables reference (so, it 1006 can be a scope gotten from on_scopes_request, the result of some evaluation, etc.). 1007 1008 Note that in the DAP the variables reference requires a unique int... the way this works for 1009 pydevd is that an instance is generated for that specific variable reference and we use its 1010 id(instance) to identify it to make sure all items are unique (and the actual {id->instance} 1011 is added to a dict which is only valid while the thread is suspended and later cleared when 1012 the related thread resumes execution). 1013 1014 see: SuspendedFramesManager 1015 1016 :param VariablesRequest request: 1017 ''' 1018 arguments = request.arguments # : :type arguments: VariablesArguments 1019 variables_reference = arguments.variablesReference 1020 1021 if isinstance(variables_reference, ScopeRequest): 1022 variables_reference = variables_reference.variable_reference 1023 1024 thread_id = py_db.suspended_frames_manager.get_thread_id_for_variable_reference( 1025 variables_reference) 1026 if thread_id is not None: 1027 self.api.request_get_variable_json(py_db, request, thread_id) 1028 else: 1029 variables = [] 1030 body = VariablesResponseBody(variables) 1031 variables_response = pydevd_base_schema.build_response(request, kwargs={ 1032 'body': body, 1033 'success': False, 1034 'message': 'Unable to find thread to evaluate variable reference.' 1035 }) 1036 return NetCommand(CMD_RETURN, 0, variables_response, is_json=True) 1037 1038 def on_setvariable_request(self, py_db, request): 1039 arguments = request.arguments # : :type arguments: SetVariableArguments 1040 variables_reference = arguments.variablesReference 1041 1042 if isinstance(variables_reference, ScopeRequest): 1043 variables_reference = variables_reference.variable_reference 1044 1045 if arguments.name.startswith('(return) '): 1046 response = pydevd_base_schema.build_response( 1047 request, 1048 kwargs={ 1049 'body': SetVariableResponseBody(''), 1050 'success': False, 1051 'message': 'Cannot change return value' 1052 }) 1053 return NetCommand(CMD_RETURN, 0, response, is_json=True) 1054 1055 thread_id = py_db.suspended_frames_manager.get_thread_id_for_variable_reference( 1056 variables_reference) 1057 1058 if thread_id is not None: 1059 self.api.request_change_variable_json(py_db, request, thread_id) 1060 else: 1061 response = pydevd_base_schema.build_response( 1062 request, 1063 kwargs={ 1064 'body': SetVariableResponseBody(''), 1065 'success': False, 1066 'message': 'Unable to find thread to evaluate variable reference.' 1067 }) 1068 return NetCommand(CMD_RETURN, 0, response, is_json=True) 1069 1070 def on_modules_request(self, py_db, request): 1071 modules_manager = py_db.cmd_factory.modules_manager # : :type modules_manager: ModulesManager 1072 modules_info = modules_manager.get_modules_info() 1073 body = ModulesResponseBody(modules_info) 1074 variables_response = pydevd_base_schema.build_response(request, kwargs={'body': body}) 1075 return NetCommand(CMD_RETURN, 0, variables_response, is_json=True) 1076 1077 def on_source_request(self, py_db, request): 1078 ''' 1079 :param SourceRequest request: 1080 ''' 1081 source_reference = request.arguments.sourceReference 1082 server_filename = None 1083 content = None 1084 1085 if source_reference != 0: 1086 server_filename = pydevd_file_utils.get_server_filename_from_source_reference(source_reference) 1087 if not server_filename: 1088 server_filename = pydevd_file_utils.get_source_reference_filename_from_linecache(source_reference) 1089 1090 if server_filename: 1091 # Try direct file access first - it's much faster when available. 1092 try: 1093 with open(server_filename, 'r') as stream: 1094 content = stream.read() 1095 except: 1096 pass 1097 1098 if content is None: 1099 # File might not exist at all, or we might not have a permission to read it, 1100 # but it might also be inside a zipfile, or an IPython cell. In this case, 1101 # linecache might still be able to retrieve the source. 1102 lines = (linecache.getline(server_filename, i) for i in itertools.count(1)) 1103 lines = itertools.takewhile(bool, lines) # empty lines are '\n', EOF is '' 1104 1105 # If we didn't get at least one line back, reset it to None so that it's 1106 # reported as error below, and not as an empty file. 1107 content = ''.join(lines) or None 1108 1109 if content is None: 1110 frame_id = pydevd_file_utils.get_frame_id_from_source_reference(source_reference) 1111 pydev_log.debug('Found frame id: %s for source reference: %s', frame_id, source_reference) 1112 if frame_id is not None: 1113 try: 1114 content = self.api.get_decompiled_source_from_frame_id(py_db, frame_id) 1115 except Exception: 1116 pydev_log.exception('Error getting source for frame id: %s', frame_id) 1117 content = None 1118 1119 body = SourceResponseBody(content or '') 1120 response_args = {'body': body} 1121 1122 if content is None: 1123 if source_reference == 0: 1124 message = 'Source unavailable' 1125 elif server_filename: 1126 message = 'Unable to retrieve source for %s' % (server_filename,) 1127 else: 1128 message = 'Invalid sourceReference %d' % (source_reference,) 1129 response_args.update({'success': False, 'message': message}) 1130 1131 response = pydevd_base_schema.build_response(request, kwargs=response_args) 1132 return NetCommand(CMD_RETURN, 0, response, is_json=True) 1133 1134 def on_gototargets_request(self, py_db, request): 1135 path = request.arguments.source.path 1136 line = request.arguments.line 1137 target_id = self._goto_targets_map.obtain_key((path, line)) 1138 target = { 1139 'id': target_id, 1140 'label': '%s:%s' % (path, line), 1141 'line': line 1142 } 1143 body = GotoTargetsResponseBody(targets=[target]) 1144 response_args = {'body': body} 1145 response = pydevd_base_schema.build_response(request, kwargs=response_args) 1146 return NetCommand(CMD_RETURN, 0, response, is_json=True) 1147 1148 def on_goto_request(self, py_db, request): 1149 target_id = int(request.arguments.targetId) 1150 thread_id = request.arguments.threadId 1151 try: 1152 path, line = self._goto_targets_map.obtain_value(target_id) 1153 except KeyError: 1154 response = pydevd_base_schema.build_response( 1155 request, 1156 kwargs={ 1157 'body': {}, 1158 'success': False, 1159 'message': 'Unknown goto target id: %d' % (target_id,), 1160 }) 1161 return NetCommand(CMD_RETURN, 0, response, is_json=True) 1162 1163 self.api.request_set_next(py_db, request.seq, thread_id, CMD_SET_NEXT_STATEMENT, path, line, '*') 1164 # See 'NetCommandFactoryJson.make_set_next_stmnt_status_message' for response 1165 return None 1166 1167 def on_setdebuggerproperty_request(self, py_db, request): 1168 args = request.arguments # : :type args: SetDebuggerPropertyArguments 1169 if args.ideOS is not None: 1170 self.api.set_ide_os(args.ideOS) 1171 1172 if args.dontTraceStartPatterns is not None and args.dontTraceEndPatterns is not None: 1173 start_patterns = tuple(args.dontTraceStartPatterns) 1174 end_patterns = tuple(args.dontTraceEndPatterns) 1175 self.api.set_dont_trace_start_end_patterns(py_db, start_patterns, end_patterns) 1176 1177 if args.skipSuspendOnBreakpointException is not None: 1178 py_db.skip_suspend_on_breakpoint_exception = tuple( 1179 get_exception_class(x) for x in args.skipSuspendOnBreakpointException) 1180 1181 if args.skipPrintBreakpointException is not None: 1182 py_db.skip_print_breakpoint_exception = tuple( 1183 get_exception_class(x) for x in args.skipPrintBreakpointException) 1184 1185 if args.multiThreadsSingleNotification is not None: 1186 py_db.multi_threads_single_notification = args.multiThreadsSingleNotification 1187 1188 # TODO: Support other common settings. Note that not all of these might be relevant to python. 1189 # JustMyCodeStepping: 0 or 1 1190 # AllowOutOfProcessSymbols: 0 or 1 1191 # DisableJITOptimization: 0 or 1 1192 # InterpreterOptions: 0 or 1 1193 # StopOnExceptionCrossingManagedBoundary: 0 or 1 1194 # WarnIfNoUserCodeOnLaunch: 0 or 1 1195 # EnableStepFiltering: true of false 1196 1197 response = pydevd_base_schema.build_response(request, kwargs={'body': {}}) 1198 return NetCommand(CMD_RETURN, 0, response, is_json=True) 1199 1200 def on_pydevdsysteminfo_request(self, py_db, request): 1201 try: 1202 pid = os.getpid() 1203 except AttributeError: 1204 pid = None 1205 1206 # It's possible to have the ppid reported from args. In this case, use that instead of the 1207 # real ppid (athough we're using `ppid`, what we want in meaning is the `launcher_pid` -- 1208 # so, if a python process is launched from another python process, consider that process the 1209 # parent and not any intermediary stubs). 1210 1211 ppid = py_db.get_arg_ppid() or self.api.get_ppid() 1212 1213 try: 1214 impl_desc = platform.python_implementation() 1215 except AttributeError: 1216 impl_desc = PY_IMPL_NAME 1217 1218 py_info = pydevd_schema.PydevdPythonInfo( 1219 version=PY_VERSION_STR, 1220 implementation=pydevd_schema.PydevdPythonImplementationInfo( 1221 name=PY_IMPL_NAME, 1222 version=PY_IMPL_VERSION_STR, 1223 description=impl_desc, 1224 ) 1225 ) 1226 platform_info = pydevd_schema.PydevdPlatformInfo(name=sys.platform) 1227 process_info = pydevd_schema.PydevdProcessInfo( 1228 pid=pid, 1229 ppid=ppid, 1230 executable=sys.executable, 1231 bitness=64 if IS_64BIT_PROCESS else 32, 1232 ) 1233 pydevd_info = pydevd_schema.PydevdInfo( 1234 usingCython=USING_CYTHON, 1235 usingFrameEval=USING_FRAME_EVAL, 1236 ) 1237 body = { 1238 'python': py_info, 1239 'platform': platform_info, 1240 'process': process_info, 1241 'pydevd': pydevd_info, 1242 } 1243 response = pydevd_base_schema.build_response(request, kwargs={'body': body}) 1244 return NetCommand(CMD_RETURN, 0, response, is_json=True) 1245 1246 def on_setpydevdsourcemap_request(self, py_db, request): 1247 args = request.arguments # : :type args: SetPydevdSourceMapArguments 1248 SourceMappingEntry = self.api.SourceMappingEntry 1249 1250 path = args.source.path 1251 source_maps = args.pydevdSourceMaps 1252 # : :type source_map: PydevdSourceMap 1253 new_mappings = [ 1254 SourceMappingEntry( 1255 source_map['line'], 1256 source_map['endLine'], 1257 source_map['runtimeLine'], 1258 self.api.filename_to_str(source_map['runtimeSource']['path']) 1259 ) for source_map in source_maps 1260 ] 1261 1262 error_msg = self.api.set_source_mapping(py_db, path, new_mappings) 1263 if error_msg: 1264 response = pydevd_base_schema.build_response( 1265 request, 1266 kwargs={ 1267 'body': {}, 1268 'success': False, 1269 'message': error_msg, 1270 }) 1271 return NetCommand(CMD_RETURN, 0, response, is_json=True) 1272 1273 response = pydevd_base_schema.build_response(request) 1274 return NetCommand(CMD_RETURN, 0, response, is_json=True) 1275 1276