1import sys 2import bisect 3import types 4 5from _pydev_imps._pydev_saved_modules import threading 6from _pydevd_bundle import pydevd_utils, pydevd_source_mapping 7from _pydevd_bundle.pydevd_additional_thread_info import set_additional_thread_info 8from _pydevd_bundle.pydevd_comm import (InternalGetThreadStack, internal_get_completions, 9 InternalSetNextStatementThread, internal_reload_code, 10 InternalGetVariable, InternalGetArray, InternalLoadFullValue, 11 internal_get_description, internal_get_frame, internal_evaluate_expression, InternalConsoleExec, 12 internal_get_variable_json, internal_change_variable, internal_change_variable_json, 13 internal_evaluate_expression_json, internal_set_expression_json, internal_get_exception_details_json, 14 internal_step_in_thread) 15from _pydevd_bundle.pydevd_comm_constants import (CMD_THREAD_SUSPEND, file_system_encoding, 16 CMD_STEP_INTO_MY_CODE, CMD_STOP_ON_START) 17from _pydevd_bundle.pydevd_constants import (get_current_thread_id, set_protocol, get_protocol, 18 HTTP_JSON_PROTOCOL, JSON_PROTOCOL, IS_PY3K, DebugInfoHolder, dict_keys, dict_items, IS_WINDOWS) 19from _pydevd_bundle.pydevd_net_command_factory_json import NetCommandFactoryJson 20from _pydevd_bundle.pydevd_net_command_factory_xml import NetCommandFactory 21import pydevd_file_utils 22from _pydev_bundle import pydev_log 23from _pydevd_bundle.pydevd_breakpoints import LineBreakpoint 24from pydevd_tracing import get_exception_traceback_str 25import os 26import subprocess 27import ctypes 28from _pydevd_bundle.pydevd_collect_bytecode_info import code_to_bytecode_representation 29import itertools 30import linecache 31from _pydevd_bundle.pydevd_utils import DAPGrouper 32from _pydevd_bundle.pydevd_daemon_thread import run_as_pydevd_daemon_thread 33from _pydevd_bundle.pydevd_thread_lifecycle import pydevd_find_thread_by_id, resume_threads 34 35try: 36 import dis 37except ImportError: 38 39 def _get_code_lines(code): 40 raise NotImplementedError 41 42else: 43 44 def _get_code_lines(code): 45 if not isinstance(code, types.CodeType): 46 path = code 47 with open(path) as f: 48 src = f.read() 49 code = compile(src, path, 'exec', 0, dont_inherit=True) 50 return _get_code_lines(code) 51 52 def iterate(): 53 # First, get all line starts for this code object. This does not include 54 # bodies of nested class and function definitions, as they have their 55 # own objects. 56 for _, lineno in dis.findlinestarts(code): 57 yield lineno 58 59 # For nested class and function definitions, their respective code objects 60 # are constants referenced by this object. 61 for const in code.co_consts: 62 if isinstance(const, types.CodeType) and const.co_filename == code.co_filename: 63 for lineno in _get_code_lines(const): 64 yield lineno 65 66 return iterate() 67 68 69class PyDevdAPI(object): 70 71 class VariablePresentation(object): 72 73 def __init__(self, special='group', function='group', class_='group', protected='inline'): 74 self._presentation = { 75 DAPGrouper.SCOPE_SPECIAL_VARS: special, 76 DAPGrouper.SCOPE_FUNCTION_VARS: function, 77 DAPGrouper.SCOPE_CLASS_VARS: class_, 78 DAPGrouper.SCOPE_PROTECTED_VARS: protected, 79 } 80 81 def get_presentation(self, scope): 82 return self._presentation[scope] 83 84 def run(self, py_db): 85 py_db.ready_to_run = True 86 87 def notify_initialize(self, py_db): 88 py_db.on_initialize() 89 90 def notify_configuration_done(self, py_db): 91 py_db.on_configuration_done() 92 93 def notify_disconnect(self, py_db): 94 py_db.on_disconnect() 95 96 def set_protocol(self, py_db, seq, protocol): 97 set_protocol(protocol.strip()) 98 if get_protocol() in (HTTP_JSON_PROTOCOL, JSON_PROTOCOL): 99 cmd_factory_class = NetCommandFactoryJson 100 else: 101 cmd_factory_class = NetCommandFactory 102 103 if not isinstance(py_db.cmd_factory, cmd_factory_class): 104 py_db.cmd_factory = cmd_factory_class() 105 106 return py_db.cmd_factory.make_protocol_set_message(seq) 107 108 def set_ide_os_and_breakpoints_by(self, py_db, seq, ide_os, breakpoints_by): 109 ''' 110 :param ide_os: 'WINDOWS' or 'UNIX' 111 :param breakpoints_by: 'ID' or 'LINE' 112 ''' 113 if breakpoints_by == 'ID': 114 py_db._set_breakpoints_with_id = True 115 else: 116 py_db._set_breakpoints_with_id = False 117 118 self.set_ide_os(ide_os) 119 120 return py_db.cmd_factory.make_version_message(seq) 121 122 def set_ide_os(self, ide_os): 123 ''' 124 :param ide_os: 'WINDOWS' or 'UNIX' 125 ''' 126 pydevd_file_utils.set_ide_os(ide_os) 127 128 def send_error_message(self, py_db, msg): 129 sys.stderr.write('pydevd: %s\n' % (msg,)) 130 131 def set_show_return_values(self, py_db, show_return_values): 132 if show_return_values: 133 py_db.show_return_values = True 134 else: 135 if py_db.show_return_values: 136 # We should remove saved return values 137 py_db.remove_return_values_flag = True 138 py_db.show_return_values = False 139 pydev_log.debug("Show return values: %s", py_db.show_return_values) 140 141 def list_threads(self, py_db, seq): 142 # Response is the command with the list of threads to be added to the writer thread. 143 return py_db.cmd_factory.make_list_threads_message(py_db, seq) 144 145 def request_suspend_thread(self, py_db, thread_id='*'): 146 # Yes, thread suspend is done at this point, not through an internal command. 147 threads = [] 148 suspend_all = thread_id.strip() == '*' 149 if suspend_all: 150 threads = pydevd_utils.get_non_pydevd_threads() 151 152 elif thread_id.startswith('__frame__:'): 153 sys.stderr.write("Can't suspend tasklet: %s\n" % (thread_id,)) 154 155 else: 156 threads = [pydevd_find_thread_by_id(thread_id)] 157 158 for t in threads: 159 if t is None: 160 continue 161 py_db.set_suspend( 162 t, 163 CMD_THREAD_SUSPEND, 164 suspend_other_threads=suspend_all, 165 is_pause=True, 166 ) 167 # Break here (even if it's suspend all) as py_db.set_suspend will 168 # take care of suspending other threads. 169 break 170 171 def set_enable_thread_notifications(self, py_db, enable): 172 ''' 173 When disabled, no thread notifications (for creation/removal) will be 174 issued until it's re-enabled. 175 176 Note that when it's re-enabled, a creation notification will be sent for 177 all existing threads even if it was previously sent (this is meant to 178 be used on disconnect/reconnect). 179 ''' 180 py_db.set_enable_thread_notifications(enable) 181 182 def request_disconnect(self, py_db, resume_threads): 183 self.set_enable_thread_notifications(py_db, False) 184 self.remove_all_breakpoints(py_db, filename='*') 185 self.remove_all_exception_breakpoints(py_db) 186 self.notify_disconnect(py_db) 187 188 if resume_threads: 189 self.request_resume_thread(thread_id='*') 190 191 def request_resume_thread(self, thread_id): 192 resume_threads(thread_id) 193 194 def request_completions(self, py_db, seq, thread_id, frame_id, act_tok, line=-1, column=-1): 195 py_db.post_method_as_internal_command( 196 thread_id, internal_get_completions, seq, thread_id, frame_id, act_tok, line=line, column=column) 197 198 def request_stack(self, py_db, seq, thread_id, fmt=None, timeout=.5, start_frame=0, levels=0): 199 # If it's already suspended, get it right away. 200 internal_get_thread_stack = InternalGetThreadStack( 201 seq, thread_id, py_db, set_additional_thread_info, fmt=fmt, timeout=timeout, start_frame=start_frame, levels=levels) 202 if internal_get_thread_stack.can_be_executed_by(get_current_thread_id(threading.current_thread())): 203 internal_get_thread_stack.do_it(py_db) 204 else: 205 py_db.post_internal_command(internal_get_thread_stack, '*') 206 207 def request_exception_info_json(self, py_db, request, thread_id, max_frames): 208 py_db.post_method_as_internal_command( 209 thread_id, 210 internal_get_exception_details_json, 211 request, 212 thread_id, 213 max_frames, 214 set_additional_thread_info=set_additional_thread_info, 215 iter_visible_frames_info=py_db.cmd_factory._iter_visible_frames_info, 216 ) 217 218 def request_step(self, py_db, thread_id, step_cmd_id): 219 t = pydevd_find_thread_by_id(thread_id) 220 if t: 221 py_db.post_method_as_internal_command( 222 thread_id, 223 internal_step_in_thread, 224 thread_id, 225 step_cmd_id, 226 set_additional_thread_info=set_additional_thread_info, 227 ) 228 elif thread_id.startswith('__frame__:'): 229 sys.stderr.write("Can't make tasklet step command: %s\n" % (thread_id,)) 230 231 def request_set_next(self, py_db, seq, thread_id, set_next_cmd_id, original_filename, line, func_name): 232 ''' 233 :param Optional[str] original_filename: 234 If available, the filename may be source translated, otherwise no translation will take 235 place (the set next just needs the line afterwards as it executes locally, but for 236 the Jupyter integration, the source mapping may change the actual lines and not only 237 the filename). 238 ''' 239 t = pydevd_find_thread_by_id(thread_id) 240 if t: 241 if original_filename is not None: 242 translated_filename = self.filename_to_server(original_filename) # Apply user path mapping. 243 pydev_log.debug('Set next (after path translation) in: %s line: %s', translated_filename, line) 244 func_name = self.to_str(func_name) 245 246 assert translated_filename.__class__ == str # i.e.: bytes on py2 and str on py3 247 assert func_name.__class__ == str # i.e.: bytes on py2 and str on py3 248 249 # Apply source mapping (i.e.: ipython). 250 _source_mapped_filename, new_line, multi_mapping_applied = py_db.source_mapping.map_to_server( 251 translated_filename, line) 252 if multi_mapping_applied: 253 pydev_log.debug('Set next (after source mapping) in: %s line: %s', translated_filename, line) 254 line = new_line 255 256 int_cmd = InternalSetNextStatementThread(thread_id, set_next_cmd_id, line, func_name, seq=seq) 257 py_db.post_internal_command(int_cmd, thread_id) 258 elif thread_id.startswith('__frame__:'): 259 sys.stderr.write("Can't set next statement in tasklet: %s\n" % (thread_id,)) 260 261 def request_reload_code(self, py_db, seq, module_name): 262 thread_id = '*' # Any thread 263 # Note: not going for the main thread because in this case it'd only do the load 264 # when we stopped on a breakpoint. 265 py_db.post_method_as_internal_command( 266 thread_id, internal_reload_code, seq, module_name) 267 268 def request_change_variable(self, py_db, seq, thread_id, frame_id, scope, attr, value): 269 ''' 270 :param scope: 'FRAME' or 'GLOBAL' 271 ''' 272 py_db.post_method_as_internal_command( 273 thread_id, internal_change_variable, seq, thread_id, frame_id, scope, attr, value) 274 275 def request_get_variable(self, py_db, seq, thread_id, frame_id, scope, attrs): 276 ''' 277 :param scope: 'FRAME' or 'GLOBAL' 278 ''' 279 int_cmd = InternalGetVariable(seq, thread_id, frame_id, scope, attrs) 280 py_db.post_internal_command(int_cmd, thread_id) 281 282 def request_get_array(self, py_db, seq, roffset, coffset, rows, cols, fmt, thread_id, frame_id, scope, attrs): 283 int_cmd = InternalGetArray(seq, roffset, coffset, rows, cols, fmt, thread_id, frame_id, scope, attrs) 284 py_db.post_internal_command(int_cmd, thread_id) 285 286 def request_load_full_value(self, py_db, seq, thread_id, frame_id, vars): 287 int_cmd = InternalLoadFullValue(seq, thread_id, frame_id, vars) 288 py_db.post_internal_command(int_cmd, thread_id) 289 290 def request_get_description(self, py_db, seq, thread_id, frame_id, expression): 291 py_db.post_method_as_internal_command( 292 thread_id, internal_get_description, seq, thread_id, frame_id, expression) 293 294 def request_get_frame(self, py_db, seq, thread_id, frame_id): 295 py_db.post_method_as_internal_command( 296 thread_id, internal_get_frame, seq, thread_id, frame_id) 297 298 def to_str(self, s): 299 ''' 300 In py2 converts a unicode to str (bytes) using utf-8. 301 -- in py3 raises an error if it's not str already. 302 ''' 303 if s.__class__ != str: 304 if not IS_PY3K: 305 s = s.encode('utf-8') 306 else: 307 raise AssertionError('Expected to have str on Python 3. Found: %s (%s)' % (s, s.__class__)) 308 return s 309 310 def filename_to_str(self, filename): 311 ''' 312 In py2 converts a unicode to str (bytes) using the file system encoding. 313 -- in py3 raises an error if it's not str already. 314 ''' 315 if filename.__class__ != str: 316 if not IS_PY3K: 317 filename = filename.encode(file_system_encoding) 318 else: 319 raise AssertionError('Expected to have str on Python 3. Found: %s (%s)' % (filename, filename.__class__)) 320 return filename 321 322 def filename_to_server(self, filename): 323 filename = self.filename_to_str(filename) 324 filename = pydevd_file_utils.map_file_to_server(filename) 325 return filename 326 327 class _DummyFrame(object): 328 ''' 329 Dummy frame to be used with PyDB.apply_files_filter (as we don't really have the 330 related frame as breakpoints are added before execution). 331 ''' 332 333 class _DummyCode(object): 334 335 def __init__(self, filename): 336 self.co_firstlineno = 1 337 self.co_filename = filename 338 self.co_name = 'invalid func name ' 339 340 def __init__(self, filename): 341 self.f_code = self._DummyCode(filename) 342 self.f_globals = {} 343 344 ADD_BREAKPOINT_NO_ERROR = 0 345 ADD_BREAKPOINT_FILE_NOT_FOUND = 1 346 ADD_BREAKPOINT_FILE_EXCLUDED_BY_FILTERS = 2 347 348 class _AddBreakpointResult(object): 349 350 # :see: ADD_BREAKPOINT_NO_ERROR = 0 351 # :see: ADD_BREAKPOINT_FILE_NOT_FOUND = 1 352 # :see: ADD_BREAKPOINT_FILE_EXCLUDED_BY_FILTERS = 2 353 354 __slots__ = ['error_code', 'translated_filename', 'translated_line'] 355 356 def __init__(self, translated_filename, translated_line): 357 self.error_code = PyDevdAPI.ADD_BREAKPOINT_NO_ERROR 358 self.translated_filename = translated_filename 359 self.translated_line = translated_line 360 361 def add_breakpoint( 362 self, py_db, original_filename, breakpoint_type, breakpoint_id, line, condition, func_name, 363 expression, suspend_policy, hit_condition, is_logpoint, adjust_line=False): 364 ''' 365 :param str original_filename: 366 Note: must be sent as it was received in the protocol. It may be translated in this 367 function and its final value will be available in the returned _AddBreakpointResult. 368 369 :param str breakpoint_type: 370 One of: 'python-line', 'django-line', 'jinja2-line'. 371 372 :param int breakpoint_id: 373 374 :param int line: 375 Note: it's possible that a new line was actually used. If that's the case its 376 final value will be available in the returned _AddBreakpointResult. 377 378 :param condition: 379 Either None or the condition to activate the breakpoint. 380 381 :param str func_name: 382 If "None" (str), may hit in any context. 383 Empty string will hit only top level. 384 Any other value must match the scope of the method to be matched. 385 386 :param str expression: 387 None or the expression to be evaluated. 388 389 :param suspend_policy: 390 Either "NONE" (to suspend only the current thread when the breakpoint is hit) or 391 "ALL" (to suspend all threads when a breakpoint is hit). 392 393 :param str hit_condition: 394 An expression where `@HIT@` will be replaced by the number of hits. 395 i.e.: `@HIT@ == x` or `@HIT@ >= x` 396 397 :param bool is_logpoint: 398 If True and an expression is passed, pydevd will create an io message command with the 399 result of the evaluation. 400 401 :return _AddBreakpointResult: 402 ''' 403 assert original_filename.__class__ == str, 'Expected str, found: %s' % (original_filename.__class__,) # i.e.: bytes on py2 and str on py3 404 405 pydev_log.debug('Request for breakpoint in: %s line: %s', original_filename, line) 406 # Parameters to reapply breakpoint. 407 api_add_breakpoint_params = (original_filename, breakpoint_type, breakpoint_id, line, condition, func_name, 408 expression, suspend_policy, hit_condition, is_logpoint) 409 410 translated_filename = self.filename_to_server(original_filename) # Apply user path mapping. 411 pydev_log.debug('Breakpoint (after path translation) in: %s line: %s', translated_filename, line) 412 func_name = self.to_str(func_name) 413 414 assert translated_filename.__class__ == str # i.e.: bytes on py2 and str on py3 415 assert func_name.__class__ == str # i.e.: bytes on py2 and str on py3 416 417 # Apply source mapping (i.e.: ipython). 418 source_mapped_filename, new_line, multi_mapping_applied = py_db.source_mapping.map_to_server( 419 translated_filename, line) 420 421 if multi_mapping_applied: 422 pydev_log.debug('Breakpoint (after source mapping) in: %s line: %s', source_mapped_filename, new_line) 423 # Note that source mapping is internal and does not change the resulting filename nor line 424 # (we want the outside world to see the line in the original file and not in the ipython 425 # cell, otherwise the editor wouldn't be correct as the returned line is the line to 426 # which the breakpoint will be moved in the editor). 427 result = self._AddBreakpointResult(original_filename, line) 428 429 # If a multi-mapping was applied, consider it the canonical / source mapped version (translated to ipython cell). 430 translated_absolute_filename = source_mapped_filename 431 canonical_normalized_filename = pydevd_file_utils.normcase(source_mapped_filename) 432 line = new_line 433 434 else: 435 translated_absolute_filename = pydevd_file_utils.absolute_path(translated_filename) 436 canonical_normalized_filename = pydevd_file_utils.canonical_normalized_path(translated_filename) 437 438 if adjust_line and not translated_absolute_filename.startswith('<'): 439 # Validate breakpoints and adjust their positions. 440 try: 441 lines = sorted(_get_code_lines(translated_absolute_filename)) 442 except Exception: 443 pass 444 else: 445 if line not in lines: 446 # Adjust to the first preceding valid line. 447 idx = bisect.bisect_left(lines, line) 448 if idx > 0: 449 line = lines[idx - 1] 450 451 result = self._AddBreakpointResult(original_filename, line) 452 453 py_db.api_received_breakpoints[(original_filename, breakpoint_id)] = (canonical_normalized_filename, api_add_breakpoint_params) 454 455 if not translated_absolute_filename.startswith('<'): 456 # Note: if a mapping pointed to a file starting with '<', don't validate. 457 458 if not pydevd_file_utils.exists(translated_absolute_filename): 459 result.error_code = self.ADD_BREAKPOINT_FILE_NOT_FOUND 460 return result 461 462 if ( 463 py_db.is_files_filter_enabled and 464 not py_db.get_require_module_for_filters() and 465 py_db.apply_files_filter(self._DummyFrame(translated_absolute_filename), translated_absolute_filename, False) 466 ): 467 # Note that if `get_require_module_for_filters()` returns False, we don't do this check. 468 # This is because we don't have the module name given a file at this point (in 469 # runtime it's gotten from the frame.f_globals). 470 # An option could be calculate it based on the filename and current sys.path, 471 # but on some occasions that may be wrong (for instance with `__main__` or if 472 # the user dynamically changes the PYTHONPATH). 473 474 # Note: depending on the use-case, filters may be changed, so, keep on going and add the 475 # breakpoint even with the error code. 476 result.error_code = self.ADD_BREAKPOINT_FILE_EXCLUDED_BY_FILTERS 477 478 if breakpoint_type == 'python-line': 479 added_breakpoint = LineBreakpoint(line, condition, func_name, expression, suspend_policy, hit_condition=hit_condition, is_logpoint=is_logpoint) 480 breakpoints = py_db.breakpoints 481 file_to_id_to_breakpoint = py_db.file_to_id_to_line_breakpoint 482 supported_type = True 483 484 else: 485 add_plugin_breakpoint_result = None 486 plugin = py_db.get_plugin_lazy_init() 487 if plugin is not None: 488 add_plugin_breakpoint_result = plugin.add_breakpoint( 489 'add_line_breakpoint', py_db, breakpoint_type, canonical_normalized_filename, line, condition, expression, func_name, hit_condition=hit_condition, is_logpoint=is_logpoint) 490 491 if add_plugin_breakpoint_result is not None: 492 supported_type = True 493 added_breakpoint, breakpoints = add_plugin_breakpoint_result 494 file_to_id_to_breakpoint = py_db.file_to_id_to_plugin_breakpoint 495 else: 496 supported_type = False 497 498 if not supported_type: 499 raise NameError(breakpoint_type) 500 501 if DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS > 0: 502 pydev_log.debug('Added breakpoint:%s - line:%s - func_name:%s\n', canonical_normalized_filename, line, func_name) 503 504 if canonical_normalized_filename in file_to_id_to_breakpoint: 505 id_to_pybreakpoint = file_to_id_to_breakpoint[canonical_normalized_filename] 506 else: 507 id_to_pybreakpoint = file_to_id_to_breakpoint[canonical_normalized_filename] = {} 508 509 id_to_pybreakpoint[breakpoint_id] = added_breakpoint 510 py_db.consolidate_breakpoints(canonical_normalized_filename, id_to_pybreakpoint, breakpoints) 511 if py_db.plugin is not None: 512 py_db.has_plugin_line_breaks = py_db.plugin.has_line_breaks() 513 514 py_db.on_breakpoints_changed() 515 return result 516 517 def reapply_breakpoints(self, py_db): 518 ''' 519 Reapplies all the received breakpoints as they were received by the API (so, new 520 translations are applied). 521 ''' 522 pydev_log.debug('Reapplying breakpoints.') 523 items = dict_items(py_db.api_received_breakpoints) # Create a copy with items to reapply. 524 self.remove_all_breakpoints(py_db, '*') 525 for _key, val in items: 526 _new_filename, api_add_breakpoint_params = val 527 self.add_breakpoint(py_db, *api_add_breakpoint_params) 528 529 def remove_all_breakpoints(self, py_db, filename): 530 ''' 531 Removes all the breakpoints from a given file or from all files if filename == '*'. 532 533 :param str filename: 534 Note: must be sent as it was received in the protocol. It may be translated in this 535 function. 536 ''' 537 assert filename.__class__ == str # i.e.: bytes on py2 and str on py3 538 changed = False 539 lst = [ 540 py_db.file_to_id_to_line_breakpoint, 541 py_db.file_to_id_to_plugin_breakpoint, 542 py_db.breakpoints 543 ] 544 if hasattr(py_db, 'django_breakpoints'): 545 lst.append(py_db.django_breakpoints) 546 547 if hasattr(py_db, 'jinja2_breakpoints'): 548 lst.append(py_db.jinja2_breakpoints) 549 550 if filename == '*': 551 py_db.api_received_breakpoints.clear() 552 553 for file_to_id_to_breakpoint in lst: 554 if file_to_id_to_breakpoint: 555 file_to_id_to_breakpoint.clear() 556 changed = True 557 558 else: 559 items = dict_items(py_db.api_received_breakpoints) # Create a copy to remove items. 560 translated_filenames = [] 561 for key, val in items: 562 original_filename, _breakpoint_id = key 563 if original_filename == filename: 564 canonical_normalized_filename, _api_add_breakpoint_params = val 565 # Note: there can be actually 1:N mappings due to source mapping (i.e.: ipython). 566 translated_filenames.append(canonical_normalized_filename) 567 del py_db.api_received_breakpoints[key] 568 569 for canonical_normalized_filename in translated_filenames: 570 for file_to_id_to_breakpoint in lst: 571 if canonical_normalized_filename in file_to_id_to_breakpoint: 572 file_to_id_to_breakpoint.pop(canonical_normalized_filename, None) 573 changed = True 574 575 if changed: 576 py_db.on_breakpoints_changed(removed=True) 577 578 def remove_breakpoint(self, py_db, received_filename, breakpoint_type, breakpoint_id): 579 ''' 580 :param str received_filename: 581 Note: must be sent as it was received in the protocol. It may be translated in this 582 function. 583 584 :param str breakpoint_type: 585 One of: 'python-line', 'django-line', 'jinja2-line'. 586 587 :param int breakpoint_id: 588 ''' 589 for key, val in dict_items(py_db.api_received_breakpoints): 590 original_filename, existing_breakpoint_id = key 591 _new_filename, _api_add_breakpoint_params = val 592 if received_filename == original_filename and existing_breakpoint_id == breakpoint_id: 593 del py_db.api_received_breakpoints[key] 594 break 595 else: 596 pydev_log.info( 597 'Did not find breakpoint to remove: %s (breakpoint id: %s)', received_filename, breakpoint_id) 598 599 file_to_id_to_breakpoint = None 600 received_filename = self.filename_to_server(received_filename) 601 canonical_normalized_filename = pydevd_file_utils.canonical_normalized_path(received_filename) 602 603 if breakpoint_type == 'python-line': 604 breakpoints = py_db.breakpoints 605 file_to_id_to_breakpoint = py_db.file_to_id_to_line_breakpoint 606 607 elif py_db.plugin is not None: 608 result = py_db.plugin.get_breakpoints(py_db, breakpoint_type) 609 if result is not None: 610 file_to_id_to_breakpoint = py_db.file_to_id_to_plugin_breakpoint 611 breakpoints = result 612 613 if file_to_id_to_breakpoint is None: 614 pydev_log.critical('Error removing breakpoint. Cannot handle breakpoint of type %s', breakpoint_type) 615 616 else: 617 try: 618 id_to_pybreakpoint = file_to_id_to_breakpoint.get(canonical_normalized_filename, {}) 619 if DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS > 0: 620 existing = id_to_pybreakpoint[breakpoint_id] 621 pydev_log.info('Removed breakpoint:%s - line:%s - func_name:%s (id: %s)\n' % ( 622 canonical_normalized_filename, existing.line, existing.func_name.encode('utf-8'), breakpoint_id)) 623 624 del id_to_pybreakpoint[breakpoint_id] 625 py_db.consolidate_breakpoints(canonical_normalized_filename, id_to_pybreakpoint, breakpoints) 626 if py_db.plugin is not None: 627 py_db.has_plugin_line_breaks = py_db.plugin.has_line_breaks() 628 629 except KeyError: 630 pydev_log.info("Error removing breakpoint: Breakpoint id not found: %s id: %s. Available ids: %s\n", 631 canonical_normalized_filename, breakpoint_id, dict_keys(id_to_pybreakpoint)) 632 633 py_db.on_breakpoints_changed(removed=True) 634 635 def request_exec_or_evaluate( 636 self, py_db, seq, thread_id, frame_id, expression, is_exec, trim_if_too_big, attr_to_set_result): 637 py_db.post_method_as_internal_command( 638 thread_id, internal_evaluate_expression, 639 seq, thread_id, frame_id, expression, is_exec, trim_if_too_big, attr_to_set_result) 640 641 def request_exec_or_evaluate_json( 642 self, py_db, request, thread_id): 643 py_db.post_method_as_internal_command( 644 thread_id, internal_evaluate_expression_json, request, thread_id) 645 646 def request_set_expression_json(self, py_db, request, thread_id): 647 py_db.post_method_as_internal_command( 648 thread_id, internal_set_expression_json, request, thread_id) 649 650 def request_console_exec(self, py_db, seq, thread_id, frame_id, expression): 651 int_cmd = InternalConsoleExec(seq, thread_id, frame_id, expression) 652 py_db.post_internal_command(int_cmd, thread_id) 653 654 def request_load_source(self, py_db, seq, filename): 655 ''' 656 :param str filename: 657 Note: must be sent as it was received in the protocol. It may be translated in this 658 function. 659 ''' 660 try: 661 filename = self.filename_to_server(filename) 662 assert filename.__class__ == str # i.e.: bytes on py2 and str on py3 663 664 with open(filename, 'r') as stream: 665 source = stream.read() 666 cmd = py_db.cmd_factory.make_load_source_message(seq, source) 667 except: 668 cmd = py_db.cmd_factory.make_error_message(seq, get_exception_traceback_str()) 669 670 py_db.writer.add_command(cmd) 671 672 def get_decompiled_source_from_frame_id(self, py_db, frame_id): 673 ''' 674 :param py_db: 675 :param frame_id: 676 :throws Exception: 677 If unable to get the frame in the currently paused frames or if some error happened 678 when decompiling. 679 ''' 680 variable = py_db.suspended_frames_manager.get_variable(int(frame_id)) 681 frame = variable.value 682 683 # Check if it's in the linecache first. 684 lines = (linecache.getline(frame.f_code.co_filename, i) for i in itertools.count(1)) 685 lines = itertools.takewhile(bool, lines) # empty lines are '\n', EOF is '' 686 687 source = ''.join(lines) 688 if not source: 689 source = code_to_bytecode_representation(frame.f_code) 690 691 return source 692 693 def request_load_source_from_frame_id(self, py_db, seq, frame_id): 694 try: 695 source = self.get_decompiled_source_from_frame_id(py_db, frame_id) 696 cmd = py_db.cmd_factory.make_load_source_from_frame_id_message(seq, source) 697 except: 698 cmd = py_db.cmd_factory.make_error_message(seq, get_exception_traceback_str()) 699 700 py_db.writer.add_command(cmd) 701 702 def add_python_exception_breakpoint( 703 self, 704 py_db, 705 exception, 706 condition, 707 expression, 708 notify_on_handled_exceptions, 709 notify_on_unhandled_exceptions, 710 notify_on_user_unhandled_exceptions, 711 notify_on_first_raise_only, 712 ignore_libraries, 713 ): 714 exception_breakpoint = py_db.add_break_on_exception( 715 exception, 716 condition=condition, 717 expression=expression, 718 notify_on_handled_exceptions=notify_on_handled_exceptions, 719 notify_on_unhandled_exceptions=notify_on_unhandled_exceptions, 720 notify_on_user_unhandled_exceptions=notify_on_user_unhandled_exceptions, 721 notify_on_first_raise_only=notify_on_first_raise_only, 722 ignore_libraries=ignore_libraries, 723 ) 724 725 if exception_breakpoint is not None: 726 py_db.on_breakpoints_changed() 727 728 def add_plugins_exception_breakpoint(self, py_db, breakpoint_type, exception): 729 supported_type = False 730 plugin = py_db.get_plugin_lazy_init() 731 if plugin is not None: 732 supported_type = plugin.add_breakpoint('add_exception_breakpoint', py_db, breakpoint_type, exception) 733 734 if supported_type: 735 py_db.has_plugin_exception_breaks = py_db.plugin.has_exception_breaks() 736 py_db.on_breakpoints_changed() 737 else: 738 raise NameError(breakpoint_type) 739 740 def remove_python_exception_breakpoint(self, py_db, exception): 741 try: 742 cp = py_db.break_on_uncaught_exceptions.copy() 743 cp.pop(exception, None) 744 py_db.break_on_uncaught_exceptions = cp 745 746 cp = py_db.break_on_caught_exceptions.copy() 747 cp.pop(exception, None) 748 py_db.break_on_caught_exceptions = cp 749 750 cp = py_db.break_on_user_uncaught_exceptions.copy() 751 cp.pop(exception, None) 752 py_db.break_on_user_uncaught_exceptions = cp 753 except: 754 pydev_log.exception("Error while removing exception %s", sys.exc_info()[0]) 755 756 py_db.on_breakpoints_changed(removed=True) 757 758 def remove_plugins_exception_breakpoint(self, py_db, exception_type, exception): 759 # I.e.: no need to initialize lazy (if we didn't have it in the first place, we can't remove 760 # anything from it anyways). 761 plugin = py_db.plugin 762 if plugin is None: 763 return 764 765 supported_type = plugin.remove_exception_breakpoint(py_db, exception_type, exception) 766 767 if supported_type: 768 py_db.has_plugin_exception_breaks = py_db.plugin.has_exception_breaks() 769 else: 770 pydev_log.info('No exception of type: %s was previously registered.', exception_type) 771 772 py_db.on_breakpoints_changed(removed=True) 773 774 def remove_all_exception_breakpoints(self, py_db): 775 py_db.break_on_uncaught_exceptions = {} 776 py_db.break_on_caught_exceptions = {} 777 py_db.break_on_user_uncaught_exceptions = {} 778 779 plugin = py_db.plugin 780 if plugin is not None: 781 plugin.remove_all_exception_breakpoints(py_db) 782 py_db.on_breakpoints_changed(removed=True) 783 784 def set_project_roots(self, py_db, project_roots): 785 ''' 786 :param unicode project_roots: 787 ''' 788 py_db.set_project_roots(project_roots) 789 790 def set_stepping_resumes_all_threads(self, py_db, stepping_resumes_all_threads): 791 py_db.stepping_resumes_all_threads = stepping_resumes_all_threads 792 793 # Add it to the namespace so that it's available as PyDevdAPI.ExcludeFilter 794 from _pydevd_bundle.pydevd_filtering import ExcludeFilter # noqa 795 796 def set_exclude_filters(self, py_db, exclude_filters): 797 ''' 798 :param list(PyDevdAPI.ExcludeFilter) exclude_filters: 799 ''' 800 py_db.set_exclude_filters(exclude_filters) 801 802 def set_use_libraries_filter(self, py_db, use_libraries_filter): 803 py_db.set_use_libraries_filter(use_libraries_filter) 804 805 def request_get_variable_json(self, py_db, request, thread_id): 806 ''' 807 :param VariablesRequest request: 808 ''' 809 py_db.post_method_as_internal_command( 810 thread_id, internal_get_variable_json, request) 811 812 def request_change_variable_json(self, py_db, request, thread_id): 813 ''' 814 :param SetVariableRequest request: 815 ''' 816 py_db.post_method_as_internal_command( 817 thread_id, internal_change_variable_json, request) 818 819 def set_dont_trace_start_end_patterns(self, py_db, start_patterns, end_patterns): 820 # Note: start/end patterns normalized internally. 821 start_patterns = tuple(pydevd_file_utils.normcase(x) for x in start_patterns) 822 end_patterns = tuple(pydevd_file_utils.normcase(x) for x in end_patterns) 823 824 # After it's set the first time, we can still change it, but we need to reset the 825 # related caches. 826 reset_caches = False 827 dont_trace_start_end_patterns_previously_set = \ 828 py_db.dont_trace_external_files.__name__ == 'custom_dont_trace_external_files' 829 830 if not dont_trace_start_end_patterns_previously_set and not start_patterns and not end_patterns: 831 # If it wasn't set previously and start and end patterns are empty we don't need to do anything. 832 return 833 834 if not py_db.is_cache_file_type_empty(): 835 # i.e.: custom function set in set_dont_trace_start_end_patterns. 836 if dont_trace_start_end_patterns_previously_set: 837 reset_caches = py_db.dont_trace_external_files.start_patterns != start_patterns or \ 838 py_db.dont_trace_external_files.end_patterns != end_patterns 839 840 else: 841 reset_caches = True 842 843 def custom_dont_trace_external_files(abs_path): 844 normalized_abs_path = pydevd_file_utils.normcase(abs_path) 845 return normalized_abs_path.startswith(start_patterns) or normalized_abs_path.endswith(end_patterns) 846 847 custom_dont_trace_external_files.start_patterns = start_patterns 848 custom_dont_trace_external_files.end_patterns = end_patterns 849 py_db.dont_trace_external_files = custom_dont_trace_external_files 850 851 if reset_caches: 852 py_db.clear_dont_trace_start_end_patterns_caches() 853 854 def stop_on_entry(self): 855 main_thread = pydevd_utils.get_main_thread() 856 if main_thread is None: 857 pydev_log.critical('Could not find main thread while setting Stop on Entry.') 858 else: 859 info = set_additional_thread_info(main_thread) 860 info.pydev_original_step_cmd = CMD_STOP_ON_START 861 info.pydev_step_cmd = CMD_STEP_INTO_MY_CODE 862 863 def set_ignore_system_exit_codes(self, py_db, ignore_system_exit_codes): 864 py_db.set_ignore_system_exit_codes(ignore_system_exit_codes) 865 866 SourceMappingEntry = pydevd_source_mapping.SourceMappingEntry 867 868 def set_source_mapping(self, py_db, source_filename, mapping): 869 ''' 870 :param str source_filename: 871 The filename for the source mapping (bytes on py2 and str on py3). 872 This filename will be made absolute in this function. 873 874 :param list(SourceMappingEntry) mapping: 875 A list with the source mapping entries to be applied to the given filename. 876 877 :return str: 878 An error message if it was not possible to set the mapping or an empty string if 879 everything is ok. 880 ''' 881 source_filename = self.filename_to_server(source_filename) 882 absolute_source_filename = pydevd_file_utils.absolute_path(source_filename) 883 for map_entry in mapping: 884 map_entry.source_filename = absolute_source_filename 885 error_msg = py_db.source_mapping.set_source_mapping(absolute_source_filename, mapping) 886 if error_msg: 887 return error_msg 888 889 self.reapply_breakpoints(py_db) 890 return '' 891 892 def set_variable_presentation(self, py_db, variable_presentation): 893 assert isinstance(variable_presentation, self.VariablePresentation) 894 py_db.variable_presentation = variable_presentation 895 896 def get_ppid(self): 897 ''' 898 Provides the parent pid (even for older versions of Python on Windows). 899 ''' 900 ppid = None 901 902 try: 903 ppid = os.getppid() 904 except AttributeError: 905 pass 906 907 if ppid is None and IS_WINDOWS: 908 ppid = self._get_windows_ppid() 909 910 return ppid 911 912 def _get_windows_ppid(self): 913 this_pid = os.getpid() 914 for ppid, pid in _list_ppid_and_pid(): 915 if pid == this_pid: 916 return ppid 917 918 return None 919 920 def _terminate_child_processes_windows(self, dont_terminate_child_pids): 921 this_pid = os.getpid() 922 for _ in range(50): # Try this at most 50 times before giving up. 923 924 # Note: we can't kill the process itself with taskkill, so, we 925 # list immediate children, kill that tree and then exit this process. 926 927 children_pids = [] 928 for ppid, pid in _list_ppid_and_pid(): 929 if ppid == this_pid: 930 if pid not in dont_terminate_child_pids: 931 children_pids.append(pid) 932 933 if not children_pids: 934 break 935 else: 936 for pid in children_pids: 937 self._call( 938 ['taskkill', '/F', '/PID', str(pid), '/T'], 939 stdout=subprocess.PIPE, 940 stderr=subprocess.PIPE 941 ) 942 943 del children_pids[:] 944 945 def _terminate_child_processes_linux_and_mac(self, dont_terminate_child_pids): 946 this_pid = os.getpid() 947 948 def list_children_and_stop_forking(initial_pid, stop=True): 949 children_pids = [] 950 if stop: 951 # Ask to stop forking (shouldn't be called for this process, only subprocesses). 952 self._call( 953 ['kill', '-STOP', str(initial_pid)], 954 stdout=subprocess.PIPE, 955 stderr=subprocess.PIPE 956 ) 957 958 list_popen = self._popen( 959 ['pgrep', '-P', str(initial_pid)], 960 stdout=subprocess.PIPE, 961 stderr=subprocess.PIPE 962 ) 963 964 if list_popen is not None: 965 stdout, _ = list_popen.communicate() 966 for line in stdout.splitlines(): 967 line = line.decode('ascii').strip() 968 if line: 969 pid = str(line) 970 if pid in dont_terminate_child_pids: 971 continue 972 children_pids.append(pid) 973 # Recursively get children. 974 children_pids.extend(list_children_and_stop_forking(pid)) 975 return children_pids 976 977 previously_found = set() 978 979 for _ in range(50): # Try this at most 50 times before giving up. 980 981 children_pids = list_children_and_stop_forking(this_pid, stop=False) 982 found_new = False 983 984 for pid in children_pids: 985 if pid not in previously_found: 986 found_new = True 987 previously_found.add(pid) 988 self._call( 989 ['kill', '-KILL', str(pid)], 990 stdout=subprocess.PIPE, 991 stderr=subprocess.PIPE 992 ) 993 994 if not found_new: 995 break 996 997 def _popen(self, cmdline, **kwargs): 998 try: 999 return subprocess.Popen(cmdline, **kwargs) 1000 except: 1001 if DebugInfoHolder.DEBUG_TRACE_LEVEL >= 1: 1002 pydev_log.exception('Error running: %s' % (' '.join(cmdline))) 1003 return None 1004 1005 def _call(self, cmdline, **kwargs): 1006 try: 1007 subprocess.check_call(cmdline, **kwargs) 1008 except: 1009 if DebugInfoHolder.DEBUG_TRACE_LEVEL >= 1: 1010 pydev_log.exception('Error running: %s' % (' '.join(cmdline))) 1011 1012 def set_terminate_child_processes(self, py_db, terminate_child_processes): 1013 py_db.terminate_child_processes = terminate_child_processes 1014 1015 def terminate_process(self, py_db): 1016 ''' 1017 Terminates the current process (and child processes if the option to also terminate 1018 child processes is enabled). 1019 ''' 1020 try: 1021 if py_db.terminate_child_processes: 1022 pydev_log.debug('Terminating child processes.') 1023 if IS_WINDOWS: 1024 self._terminate_child_processes_windows(py_db.dont_terminate_child_pids) 1025 else: 1026 self._terminate_child_processes_linux_and_mac(py_db.dont_terminate_child_pids) 1027 finally: 1028 pydev_log.debug('Exiting process (os._exit(0)).') 1029 os._exit(0) 1030 1031 def _terminate_if_commands_processed(self, py_db): 1032 py_db.dispose_and_kill_all_pydevd_threads() 1033 self.terminate_process(py_db) 1034 1035 def request_terminate_process(self, py_db): 1036 # We mark with a terminate_requested to avoid that paused threads start running 1037 # (we should terminate as is without letting any paused thread run). 1038 py_db.terminate_requested = True 1039 run_as_pydevd_daemon_thread(py_db, self._terminate_if_commands_processed, py_db) 1040 1041 1042def _list_ppid_and_pid(): 1043 _TH32CS_SNAPPROCESS = 0x00000002 1044 1045 class PROCESSENTRY32(ctypes.Structure): 1046 _fields_ = [("dwSize", ctypes.c_uint32), 1047 ("cntUsage", ctypes.c_uint32), 1048 ("th32ProcessID", ctypes.c_uint32), 1049 ("th32DefaultHeapID", ctypes.c_size_t), 1050 ("th32ModuleID", ctypes.c_uint32), 1051 ("cntThreads", ctypes.c_uint32), 1052 ("th32ParentProcessID", ctypes.c_uint32), 1053 ("pcPriClassBase", ctypes.c_long), 1054 ("dwFlags", ctypes.c_uint32), 1055 ("szExeFile", ctypes.c_char * 260)] 1056 1057 kernel32 = ctypes.windll.kernel32 1058 snapshot = kernel32.CreateToolhelp32Snapshot(_TH32CS_SNAPPROCESS, 0) 1059 ppid_and_pids = [] 1060 try: 1061 process_entry = PROCESSENTRY32() 1062 process_entry.dwSize = ctypes.sizeof(PROCESSENTRY32) 1063 if not kernel32.Process32First(snapshot, ctypes.byref(process_entry)): 1064 pydev_log.critical('Process32First failed (getting process from CreateToolhelp32Snapshot).') 1065 else: 1066 while True: 1067 ppid_and_pids.append((process_entry.th32ParentProcessID, process_entry.th32ProcessID)) 1068 if not kernel32.Process32Next(snapshot, ctypes.byref(process_entry)): 1069 break 1070 finally: 1071 kernel32.CloseHandle(snapshot) 1072 1073 return ppid_and_pids 1074