def test_zip_paths(tmpdir): import pydevd_file_utils import sys import zipfile for i, zip_basename in enumerate(('MY1.zip', 'my2.egg!')): zipfile_path = str(tmpdir.join(zip_basename)) zip_file = zipfile.ZipFile(zipfile_path, 'w') zip_file.writestr('zipped%s/__init__.py' % (i, ), '') zip_file.writestr('zipped%s/zipped_contents.py' % (i, ), 'def call_in_zip():\n return 1') zip_file.close() sys.path.append(zipfile_path) try: import importlib except ImportError: __import__('zipped%s' % (i, )) # Py2.6 does not have importlib else: importlib.import_module('zipped%s' % (i, )) # Check that it's importable. # Check that we can deal with the zip path. assert pydevd_file_utils.exists(zipfile_path) abspath, realpath, basename = pydevd_file_utils.get_abs_path_real_path_and_base_from_file( zipfile_path) if IS_WINDOWS: assert abspath == zipfile_path.lower() assert basename == zip_basename.lower() else: assert abspath == zipfile_path assert basename == zip_basename # Check that we can deal with zip contents. for path in [ zipfile_path + '/zipped%s/__init__.py' % (i, ), zipfile_path + '/zipped%s/zipped_contents.py' % (i, ), zipfile_path + '\\zipped%s\\__init__.py' % (i, ), zipfile_path + '\\zipped%s\\zipped_contents.py' % (i, ), ]: assert pydevd_file_utils.exists( path), 'Expected exists to return True for path:\n%s' % ( path, ) abspath, realpath, basename = pydevd_file_utils.get_abs_path_real_path_and_base_from_file( path) assert pydevd_file_utils.exists( abspath), 'Expected exists to return True for path:\n%s' % ( abspath, ) assert pydevd_file_utils.exists( realpath), 'Expected exists to return True for path:\n%s' % ( realpath, ) assert zipfile_path in pydevd_file_utils._ZIP_SEARCH_CACHE, '%s not in %s' % ( zipfile_path, '\n'.join( sorted(pydevd_file_utils._ZIP_SEARCH_CACHE.keys())))
def _get_template_file_name(frame): try: if IS_DJANGO19_OR_HIGHER: # The Node source was removed since Django 1.9 if dict_contains(frame.f_locals, 'context'): context = frame.f_locals['context'] if hasattr(context, 'template') and hasattr(context.template, 'origin') and \ hasattr(context.template.origin, 'name'): return context.template.origin.name return None source = _get_source_django_18_or_lower(frame) if source is None: pydev_log.debug("Source is None\n") return None fname = source[0].name if fname == '<unknown source>': pydev_log.debug("Source name is %s\n" % fname) return None else: abs_path_real_path_and_base = get_abs_path_real_path_and_base_from_file( fname) return abs_path_real_path_and_base[1] except: pydev_log.debug(traceback.format_exc()) return None
def _get_jinja2_template_filename(frame): if dict_contains(frame.f_globals, '__jinja_template__'): fname = frame.f_globals['__jinja_template__'].filename abs_path_real_path_and_base = get_abs_path_real_path_and_base_from_file( fname) return abs_path_real_path_and_base[1] return None
def _get_jinja2_template_filename(frame): if '__jinja_template__' in frame.f_globals: fname = _convert_to_str(frame.f_globals['__jinja_template__'].filename) abs_path_real_path_and_base = get_abs_path_real_path_and_base_from_file( fname) return abs_path_real_path_and_base[1] return None
def debug(address, log_dir=None, multiprocess=True): if log_dir: log.log_dir = log_dir log.to_file(prefix="ptvsd.server") log.describe_environment("ptvsd.server debug start environment:") log.debug("{0}{1!r}", func.__name__, (address, log_dir, multiprocess)) if is_attached(): log.info("{0}() ignored - already attached.", func.__name__) return options.host, options.port # Ensure port is int if address is not options: host, port = address options.host, options.port = (host, int(port)) if multiprocess is not options: options.multiprocess = multiprocess ptvsd_path, _, _ = get_abs_path_real_path_and_base_from_file(ptvsd.__file__) ptvsd_path = os.path.dirname(ptvsd_path) start_patterns = (ptvsd_path,) end_patterns = ("ptvsd_launcher.py",) log.info( "Won't trace filenames starting with: {0!j}\n" "Won't trace filenames ending with: {1!j}", start_patterns, end_patterns, ) try: return func(start_patterns, end_patterns) except Exception: raise log.exception("{0}() failed:", func.__name__, level="info")
def debug(address, **kwargs): if _settrace.called: raise RuntimeError("this process already has a debug adapter") try: _, port = address except Exception: port = address address = ("127.0.0.1", port) try: port.__index__() # ensure it's int-like except Exception: raise ValueError("expected port or (host, port)") if not (0 <= port < 2**16): raise ValueError("invalid port number") ensure_logging() log.debug("{0}({1!r}, **{2!r})", func.__name__, address, kwargs) log.info("Initial debug configuration: {0!j}", _config) settrace_kwargs = { "suspend": False, "patch_multiprocessing": _config.get("subProcess", True), } debugpy_path, _, _ = get_abs_path_real_path_and_base_from_file( debugpy.__file__) debugpy_path = os.path.dirname(debugpy_path) settrace_kwargs["dont_trace_start_patterns"] = (debugpy_path, ) settrace_kwargs["dont_trace_end_patterns"] = ("debugpy_launcher.py", ) try: return func(address, settrace_kwargs, **kwargs) except Exception: log.reraise_exception("{0}() failed:", func.__name__, level="info")
def _get_template_file_name(frame): try: if IS_DJANGO19_OR_HIGHER: # The Node source was removed since Django 1.9 if dict_contains(frame.f_locals, 'context'): context = frame.f_locals['context'] if hasattr(context, 'template') and hasattr(context.template, 'origin') and \ hasattr(context.template.origin, 'name'): return context.template.origin.name return None source = _get_source_django_18_or_lower(frame) if source is None: pydev_log.debug("Source is None\n") return None fname = source[0].name if fname == '<unknown source>': pydev_log.debug("Source name is %s\n" % fname) return None else: abs_path_real_path_and_base = get_abs_path_real_path_and_base_from_file(fname) return abs_path_real_path_and_base[1] except: pydev_log.debug(traceback.format_exc()) return None
def test_relative_paths(tmpdir): ''' We need to check that we can deal with relative paths. Use cases: - Relative path of file that does not exist: Use case is a cython-generated module which is generated from a .pyx which is not distributed. In this case we need to resolve the file to a library path file. - Relative path of a file that exists but not when resolved from the working directory: Use case is a cython-generated module which is generated from a .pyx which is distributed. In this case we need to resolve to the real file based on the sys.path entries. ''' import pydevd_file_utils import sys sys.path.append(str(tmpdir)) try: pydevd_file_utils.NORM_PATHS_AND_BASE_CONTAINER.clear() pydevd_file_utils.NORM_PATHS_CONTAINER.clear() abs_path = pydevd_file_utils.get_abs_path_real_path_and_base_from_file( 'my_dir/my_file.pyx')[0] assert 'site-packages' in abs_path assert os.path.normcase(str(tmpdir)) not in abs_path assert not pydevd_file_utils.exists('my_dir/my_file.pyx') # If the relative file exists when joined with some entry in the PYTHONPATH we'll consider # that the relative path points to that absolute path. target_dir = os.path.join(str(tmpdir), 'my_dir') os.makedirs(target_dir) with open(os.path.join(target_dir, 'my_file.pyx'), 'w') as stream: stream.write('empty') pydevd_file_utils.NORM_PATHS_AND_BASE_CONTAINER.clear() pydevd_file_utils.NORM_PATHS_CONTAINER.clear() abs_path = pydevd_file_utils.get_abs_path_real_path_and_base_from_file( 'my_dir/my_file.pyx')[0] assert 'site-packages' not in abs_path assert str(tmpdir) in abs_path assert pydevd_file_utils.exists('my_dir/my_file.pyx') finally: sys.path.remove(str(tmpdir))
def _get_template_file_name(frame): try: if IS_DJANGO19: # The Node source was removed since Django 1.9 if 'context' in frame.f_locals: context = frame.f_locals['context'] if hasattr(context, '_has_included_template'): # if there was included template we need to inspect the previous frames and find its name back = frame.f_back while back is not None and frame.f_code.co_name in ( 'render', '_render'): locals = back.f_locals if 'self' in locals: self = locals['self'] if self.__class__.__name__ == 'Template' and hasattr(self, 'origin') and \ hasattr(self.origin, 'name'): return normcase( _convert_to_str(self.origin.name)) back = back.f_back else: if hasattr(context, 'template') and hasattr(context.template, 'origin') and \ hasattr(context.template.origin, 'name'): return normcase( _convert_to_str(context.template.origin.name)) return None elif IS_DJANGO19_OR_HIGHER: # For Django 1.10 and later there is much simpler way to get template name if 'self' in frame.f_locals: self = frame.f_locals['self'] if hasattr(self, 'origin') and hasattr(self.origin, 'name'): return normcase(_convert_to_str(self.origin.name)) return None source = _get_source_django_18_or_lower(frame) if source is None: pydev_log.debug("Source is None\n") return None fname = _convert_to_str(source[0].name) if fname == '<unknown source>': pydev_log.debug("Source name is %s\n" % fname) return None else: abs_path_real_path_and_base = get_abs_path_real_path_and_base_from_file( fname) return abs_path_real_path_and_base[1] except: pydev_log.debug(traceback.format_exc()) return None
def _get_template_file_name(frame): try: if IS_DJANGO19: # The Node source was removed since Django 1.9 if 'context' in frame.f_locals: context = frame.f_locals['context'] if hasattr(context, '_has_included_template'): # if there was included template we need to inspect the previous frames and find its name back = frame.f_back while back is not None and frame.f_code.co_name in ('render', '_render'): locals = back.f_locals if 'self' in locals: self = locals['self'] if self.__class__.__name__ == 'Template' and hasattr(self, 'origin') and \ hasattr(self.origin, 'name'): return normcase(self.origin.name) back = back.f_back else: if hasattr(context, 'template') and hasattr(context.template, 'origin') and \ hasattr(context.template.origin, 'name'): return normcase(context.template.origin.name) return None elif IS_DJANGO19_OR_HIGHER: # For Django 1.10 and later there is much simpler way to get template name if 'self' in frame.f_locals: self = frame.f_locals['self'] if hasattr(self, 'origin') and hasattr(self.origin, 'name'): return normcase(self.origin.name) return None source = _get_source_django_18_or_lower(frame) if source is None: pydev_log.debug("Source is None\n") return None fname = source[0].name if fname == '<unknown source>': pydev_log.debug("Source name is %s\n" % fname) return None else: abs_path_real_path_and_base = get_abs_path_real_path_and_base_from_file(fname) return abs_path_real_path_and_base[1] except: pydev_log.debug(traceback.format_exc()) return None
def _normpath(filename): return pydevd_file_utils.get_abs_path_real_path_and_base_from_file(filename)[0]
def _get_jinja2_template_filename(frame): if '__jinja_template__' in frame.f_globals: fname = frame.f_globals['__jinja_template__'].filename abs_path_real_path_and_base = get_abs_path_real_path_and_base_from_file(fname) return abs_path_real_path_and_base[1] return None
def _normpath(self, filename): return pydevd_file_utils.get_abs_path_real_path_and_base_from_file(filename)[0]
def trace_dispatch(self, frame, event, arg): # ENDIF # DEBUG = 'code_to_debug' in frame.f_code.co_filename main_debugger, filename, info, thread, frame_skips_cache, frame_cache_key = self._args # if DEBUG: print('frame trace_dispatch %s %s %s %s %s' % (frame.f_lineno, frame.f_code.co_name, frame.f_code.co_filename, event, info.pydev_step_cmd)) try: info.is_tracing = True line = frame.f_lineno line_cache_key = (frame_cache_key, line) if main_debugger._finish_debugging_session: return None if event == 'call' else NO_FTRACE plugin_manager = main_debugger.plugin is_exception_event = event == 'exception' has_exception_breakpoints = main_debugger.break_on_caught_exceptions or main_debugger.has_plugin_exception_breaks if is_exception_event: if has_exception_breakpoints: should_stop, frame = self.should_stop_on_exception(frame, event, arg) if should_stop: self.handle_exception(frame, event, arg) return self.trace_dispatch is_line = False is_return = False is_call = False else: is_line = event == 'line' is_return = event == 'return' is_call = event == 'call' if not is_line and not is_return and not is_call: # Unexpected: just keep the same trace func. return self.trace_dispatch need_signature_trace_return = False if main_debugger.signature_factory is not None: if is_call: need_signature_trace_return = send_signature_call_trace(main_debugger, frame, filename) elif is_return: send_signature_return_trace(main_debugger, frame, filename, arg) stop_frame = info.pydev_step_stop step_cmd = info.pydev_step_cmd if is_exception_event: breakpoints_for_file = None # CMD_STEP_OVER = 108, CMD_STEP_OVER_MY_CODE = 159 if stop_frame and stop_frame is not frame and step_cmd in (108, 159) and \ arg[0] in (StopIteration, GeneratorExit) and arg[2] is None: if step_cmd == CMD_STEP_OVER: info.pydev_step_cmd = CMD_STEP_INTO else: info.pydev_step_cmd = CMD_STEP_INTO_MY_CODE info.pydev_step_stop = None else: # If we are in single step mode and something causes us to exit the current frame, we need to make sure we break # eventually. Force the step mode to step into and the step stop frame to None. # I.e.: F6 in the end of a function should stop in the next possible position (instead of forcing the user # to make a step in or step over at that location). # Note: this is especially troublesome when we're skipping code with the # @DontTrace comment. if stop_frame is frame and is_return and step_cmd in (108, 109, 159, 160): # CMD_STEP_OVER = 108, CMD_STEP_RETURN = 109, CMD_STEP_OVER_MY_CODE = 159, CMD_STEP_RETURN_MY_CODE = 160 if not frame.f_code.co_flags & 0x20: # CO_GENERATOR = 0x20 (inspect.CO_GENERATOR) if step_cmd in (108, 109): info.pydev_step_cmd = CMD_STEP_INTO else: info.pydev_step_cmd = CMD_STEP_INTO_MY_CODE info.pydev_step_stop = None breakpoints_for_file = main_debugger.breakpoints.get(filename) can_skip = False if info.pydev_state == 1: # STATE_RUN = 1 # we can skip if: # - we have no stop marked # - we should make a step return/step over and we're not in the current frame # CMD_STEP_OVER = 108, CMD_STEP_RETURN = 109, CMD_STEP_OVER_MY_CODE = 159, CMD_STEP_RETURN_MY_CODE = 160 can_skip = (step_cmd == -1 and stop_frame is None) \ or (step_cmd in (108, 109, 159, 160) and stop_frame is not frame) if can_skip: if plugin_manager is not None and ( main_debugger.has_plugin_line_breaks or main_debugger.has_plugin_exception_breaks): can_skip = plugin_manager.can_skip(main_debugger, frame) # CMD_STEP_OVER = 108, CMD_STEP_OVER_MY_CODE = 159 if can_skip and main_debugger.show_return_values and info.pydev_step_cmd in (108, 159) and frame.f_back is info.pydev_step_stop: # trace function for showing return values after step over can_skip = False # Let's check to see if we are in a function that has a breakpoint. If we don't have a breakpoint, # we will return nothing for the next trace # also, after we hit a breakpoint and go to some other debugging state, we have to force the set trace anyway, # so, that's why the additional checks are there. if not breakpoints_for_file: if can_skip: if has_exception_breakpoints: return self.trace_exception else: if need_signature_trace_return: return self.trace_return else: return None if is_call else NO_FTRACE else: # When cached, 0 means we don't have a breakpoint and 1 means we have. if can_skip: breakpoints_in_line_cache = frame_skips_cache.get(line_cache_key, -1) if breakpoints_in_line_cache == 0: return self.trace_dispatch breakpoints_in_frame_cache = frame_skips_cache.get(frame_cache_key, -1) if breakpoints_in_frame_cache != -1: # Gotten from cache. has_breakpoint_in_frame = breakpoints_in_frame_cache == 1 else: has_breakpoint_in_frame = False # Checks the breakpoint to see if there is a context match in some function curr_func_name = frame.f_code.co_name # global context is set with an empty name if curr_func_name in ('?', '<module>', '<lambda>'): curr_func_name = '' for breakpoint in dict_iter_values(breakpoints_for_file): # jython does not support itervalues() # will match either global or some function if breakpoint.func_name in ('None', curr_func_name): has_breakpoint_in_frame = True break # Cache the value (1 or 0 or -1 for default because of cython). if has_breakpoint_in_frame: frame_skips_cache[frame_cache_key] = 1 else: frame_skips_cache[frame_cache_key] = 0 if can_skip and not has_breakpoint_in_frame: if has_exception_breakpoints: return self.trace_exception else: if need_signature_trace_return: return self.trace_return else: return None if is_call else NO_FTRACE # We may have hit a breakpoint or we are already in step mode. Either way, let's check what we should do in this frame # if DEBUG: print('NOT skipped: %s %s %s %s' % (frame.f_lineno, frame.f_code.co_name, event, frame.__class__.__name__)) try: flag = False # return is not taken into account for breakpoint hit because we'd have a double-hit in this case # (one for the line and the other for the return). stop_info = {} breakpoint = None exist_result = False stop = False bp_type = None if not is_return and info.pydev_state != STATE_SUSPEND and breakpoints_for_file is not None and line in breakpoints_for_file: breakpoint = breakpoints_for_file[line] new_frame = frame stop = True if step_cmd in (CMD_STEP_OVER, CMD_STEP_OVER_MY_CODE) and (stop_frame is frame and is_line): stop = False # we don't stop on breakpoint if we have to stop by step-over (it will be processed later) elif plugin_manager is not None and main_debugger.has_plugin_line_breaks: result = plugin_manager.get_breakpoint(main_debugger, self, frame, event, self._args) if result: exist_result = True flag, breakpoint, new_frame, bp_type = result if breakpoint: # ok, hit breakpoint, now, we have to discover if it is a conditional breakpoint # lets do the conditional stuff here if stop or exist_result: eval_result = False if breakpoint.has_condition: eval_result = main_debugger.handle_breakpoint_condition(info, breakpoint, new_frame) if breakpoint.expression is not None: main_debugger.handle_breakpoint_expression(breakpoint, info, new_frame) if breakpoint.is_logpoint and info.pydev_message is not None and len(info.pydev_message) > 0: cmd = main_debugger.cmd_factory.make_io_message(info.pydev_message + os.linesep, '1') main_debugger.writer.add_command(cmd) if breakpoint.has_condition: if not eval_result: return self.trace_dispatch elif breakpoint.is_logpoint: return self.trace_dispatch if is_call and frame.f_code.co_name in ('<module>', '<lambda>'): # If we find a call for a module, it means that the module is being imported/executed for the # first time. In this case we have to ignore this hit as it may later duplicated by a # line event at the same place (so, if there's a module with a print() in the first line # the user will hit that line twice, which is not what we want). # # As for lambda, as it only has a single statement, it's not interesting to trace # its call and later its line event as they're usually in the same line. return self.trace_dispatch if main_debugger.show_return_values: if is_return and info.pydev_step_cmd in (CMD_STEP_OVER, CMD_STEP_OVER_MY_CODE) and frame.f_back == info.pydev_step_stop: self.show_return_values(frame, arg) elif main_debugger.remove_return_values_flag: try: self.remove_return_values(main_debugger, frame) finally: main_debugger.remove_return_values_flag = False if stop: self.set_suspend( thread, CMD_SET_BREAK, suspend_other_threads=breakpoint and breakpoint.suspend_policy == "ALL", ) elif flag and plugin_manager is not None: result = plugin_manager.suspend(main_debugger, thread, frame, bp_type) if result: frame = result # if thread has a suspend flag, we suspend with a busy wait if info.pydev_state == STATE_SUSPEND: self.do_wait_suspend(thread, frame, event, arg) return self.trace_dispatch else: if not breakpoint and is_line: # No stop from anyone and no breakpoint found in line (cache that). frame_skips_cache[line_cache_key] = 0 except: traceback.print_exc() raise # step handling. We stop when we hit the right frame try: should_skip = 0 if pydevd_dont_trace.should_trace_hook is not None: if self.should_skip == -1: # I.e.: cache the result on self.should_skip (no need to evaluate the same frame multiple times). # Note that on a code reload, we won't re-evaluate this because in practice, the frame.f_code # Which will be handled by this frame is read-only, so, we can cache it safely. if not pydevd_dont_trace.should_trace_hook(frame, filename): # -1, 0, 1 to be Cython-friendly should_skip = self.should_skip = 1 else: should_skip = self.should_skip = 0 else: should_skip = self.should_skip plugin_stop = False if should_skip: stop = False elif step_cmd in (CMD_STEP_INTO, CMD_STEP_INTO_MY_CODE): force_check_project_scope = step_cmd == CMD_STEP_INTO_MY_CODE if is_line: if force_check_project_scope or main_debugger.is_files_filter_enabled: stop = not main_debugger.apply_files_filter(frame, frame.f_code.co_filename, force_check_project_scope) else: stop = True elif is_return and frame.f_back is not None: if main_debugger.get_file_type( get_abs_path_real_path_and_base_from_frame(frame.f_back)) == main_debugger.PYDEV_FILE: stop = False else: if force_check_project_scope or main_debugger.is_files_filter_enabled: stop = not main_debugger.apply_files_filter(frame.f_back, frame.f_back.f_code.co_filename, force_check_project_scope) else: stop = True if plugin_manager is not None: result = plugin_manager.cmd_step_into(main_debugger, frame, event, self._args, stop_info, stop) if result: stop, plugin_stop = result elif step_cmd in (CMD_STEP_OVER, CMD_STEP_OVER_MY_CODE): # Note: when dealing with a step over my code it's the same as a step over (the # difference is that when we return from a frame in one we go to regular step # into and in the other we go to a step into my code). stop = stop_frame is frame and is_line # Note: don't stop on a return for step over, only for line events # i.e.: don't stop in: (stop_frame is frame.f_back and is_return) as we'd stop twice in that line. if frame.f_code.co_flags & CO_GENERATOR: if is_return: stop = False if plugin_manager is not None: result = plugin_manager.cmd_step_over(main_debugger, frame, event, self._args, stop_info, stop) if result: stop, plugin_stop = result elif step_cmd == CMD_SMART_STEP_INTO: stop = False if info.pydev_smart_step_stop is frame: info.pydev_func_name = '.invalid.' # Must match the type in cython info.pydev_smart_step_stop = None if is_line or is_exception_event: curr_func_name = frame.f_code.co_name # global context is set with an empty name if curr_func_name in ('?', '<module>') or curr_func_name is None: curr_func_name = '' if curr_func_name == info.pydev_func_name: stop = True elif step_cmd in (CMD_STEP_RETURN, CMD_STEP_RETURN_MY_CODE): stop = is_return and stop_frame is frame else: stop = False if stop and step_cmd != -1 and is_return and IS_PY3K and hasattr(frame, "f_back"): f_code = getattr(frame.f_back, 'f_code', None) if f_code is not None: if main_debugger.get_file_type( get_abs_path_real_path_and_base_from_file(f_code.co_filename)) == main_debugger.PYDEV_FILE: stop = False if plugin_stop: stopped_on_plugin = plugin_manager.stop(main_debugger, frame, event, self._args, stop_info, arg, step_cmd) elif stop: if is_line: self.set_suspend(thread, step_cmd) self.do_wait_suspend(thread, frame, event, arg) else: # return event back = frame.f_back if back is not None: # When we get to the pydevd run function, the debugging has actually finished for the main thread # (note that it can still go on for other threads, but for this one, we just make it finish) # So, just setting it to None should be OK _, back_filename, base = get_abs_path_real_path_and_base_from_frame(back) if (base, back.f_code.co_name) in (DEBUG_START, DEBUG_START_PY3K): back = None elif base == TRACE_PROPERTY: # We dont want to trace the return event of pydevd_traceproperty (custom property for debugging) # if we're in a return, we want it to appear to the user in the previous frame! return None if is_call else NO_FTRACE elif pydevd_dont_trace.should_trace_hook is not None: if not pydevd_dont_trace.should_trace_hook(back, back_filename): # In this case, we'll have to skip the previous one because it shouldn't be traced. # Also, we have to reset the tracing, because if the parent's parent (or some # other parent) has to be traced and it's not currently, we wouldn't stop where # we should anymore (so, a step in/over/return may not stop anywhere if no parent is traced). # Related test: _debugger_case17a.py main_debugger.set_trace_for_frame_and_parents(back) return None if is_call else NO_FTRACE if back is not None: # if we're in a return, we want it to appear to the user in the previous frame! self.set_suspend(thread, step_cmd) self.do_wait_suspend(thread, back, event, arg) else: # in jython we may not have a back frame info.pydev_step_stop = None info.pydev_step_cmd = -1 info.pydev_state = STATE_RUN except KeyboardInterrupt: raise except: try: traceback.print_exc() info.pydev_step_cmd = -1 except: return None if is_call else NO_FTRACE # if we are quitting, let's stop the tracing if not main_debugger.quitting: return self.trace_dispatch else: return None if is_call else NO_FTRACE finally: info.is_tracing = False
def trace_dispatch(self, frame, event, arg): # ENDIF # DEBUG = 'code_to_debug' in frame.f_code.co_filename main_debugger, filename, info, thread, frame_skips_cache, frame_cache_key = self._args # if DEBUG: print('frame trace_dispatch %s %s %s %s %s' % (frame.f_lineno, frame.f_code.co_name, frame.f_code.co_filename, event, info.pydev_step_cmd)) try: info.is_tracing = True line = frame.f_lineno line_cache_key = (frame_cache_key, line) if main_debugger._finish_debugging_session: return None if event == 'call' else NO_FTRACE plugin_manager = main_debugger.plugin is_exception_event = event == 'exception' has_exception_breakpoints = main_debugger.break_on_caught_exceptions or main_debugger.has_plugin_exception_breaks if is_exception_event: if has_exception_breakpoints: should_stop, frame = self.should_stop_on_exception(frame, event, arg) if should_stop: self.handle_exception(frame, event, arg) return self.trace_dispatch is_line = False is_return = False is_call = False else: is_line = event == 'line' is_return = event == 'return' is_call = event == 'call' if not is_line and not is_return and not is_call: # Unexpected: just keep the same trace func. return self.trace_dispatch need_signature_trace_return = False if main_debugger.signature_factory is not None: if is_call: need_signature_trace_return = send_signature_call_trace(main_debugger, frame, filename) elif is_return: send_signature_return_trace(main_debugger, frame, filename, arg) stop_frame = info.pydev_step_stop step_cmd = info.pydev_step_cmd if is_exception_event: breakpoints_for_file = None # CMD_STEP_OVER = 108 if stop_frame and stop_frame is not frame and step_cmd == 108 and \ arg[0] in (StopIteration, GeneratorExit) and arg[2] is None: info.pydev_step_cmd = 107 # CMD_STEP_INTO = 107 info.pydev_step_stop = None else: # If we are in single step mode and something causes us to exit the current frame, we need to make sure we break # eventually. Force the step mode to step into and the step stop frame to None. # I.e.: F6 in the end of a function should stop in the next possible position (instead of forcing the user # to make a step in or step over at that location). # Note: this is especially troublesome when we're skipping code with the # @DontTrace comment. if stop_frame is frame and is_return and step_cmd in (109, 108): # CMD_STEP_RETURN = 109, CMD_STEP_OVER = 108 if not frame.f_code.co_flags & 0x20: # CO_GENERATOR = 0x20 (inspect.CO_GENERATOR) info.pydev_step_cmd = 107 # CMD_STEP_INTO = 107 info.pydev_step_stop = None breakpoints_for_file = main_debugger.breakpoints.get(filename) can_skip = False if info.pydev_state == 1: # STATE_RUN = 1 # we can skip if: # - we have no stop marked # - we should make a step return/step over and we're not in the current frame # CMD_STEP_RETURN = 109, CMD_STEP_OVER = 108 can_skip = (step_cmd == -1 and stop_frame is None) \ or (step_cmd in (109, 108) and stop_frame is not frame) if can_skip: if plugin_manager is not None and main_debugger.has_plugin_line_breaks: can_skip = not plugin_manager.can_not_skip(main_debugger, self, frame) # CMD_STEP_OVER = 108 if can_skip and main_debugger.show_return_values and info.pydev_step_cmd == 108 and frame.f_back is info.pydev_step_stop: # trace function for showing return values after step over can_skip = False # Let's check to see if we are in a function that has a breakpoint. If we don't have a breakpoint, # we will return nothing for the next trace # also, after we hit a breakpoint and go to some other debugging state, we have to force the set trace anyway, # so, that's why the additional checks are there. if not breakpoints_for_file: if can_skip: if has_exception_breakpoints: return self.trace_exception else: if need_signature_trace_return: return self.trace_return else: return None if is_call else NO_FTRACE else: # When cached, 0 means we don't have a breakpoint and 1 means we have. if can_skip: breakpoints_in_line_cache = frame_skips_cache.get(line_cache_key, -1) if breakpoints_in_line_cache == 0: return self.trace_dispatch breakpoints_in_frame_cache = frame_skips_cache.get(frame_cache_key, -1) if breakpoints_in_frame_cache != -1: # Gotten from cache. has_breakpoint_in_frame = breakpoints_in_frame_cache == 1 else: has_breakpoint_in_frame = False # Checks the breakpoint to see if there is a context match in some function curr_func_name = frame.f_code.co_name # global context is set with an empty name if curr_func_name in ('?', '<module>', '<lambda>'): curr_func_name = '' for breakpoint in dict_iter_values(breakpoints_for_file): # jython does not support itervalues() # will match either global or some function if breakpoint.func_name in ('None', curr_func_name): has_breakpoint_in_frame = True break # Cache the value (1 or 0 or -1 for default because of cython). if has_breakpoint_in_frame: frame_skips_cache[frame_cache_key] = 1 else: frame_skips_cache[frame_cache_key] = 0 if can_skip and not has_breakpoint_in_frame: if has_exception_breakpoints: return self.trace_exception else: if need_signature_trace_return: return self.trace_return else: return None if is_call else NO_FTRACE # We may have hit a breakpoint or we are already in step mode. Either way, let's check what we should do in this frame # if DEBUG: print('NOT skipped: %s %s %s %s' % (frame.f_lineno, frame.f_code.co_name, event, frame.__class__.__name__)) try: flag = False # return is not taken into account for breakpoint hit because we'd have a double-hit in this case # (one for the line and the other for the return). stop_info = {} breakpoint = None exist_result = False stop = False bp_type = None if not is_return and info.pydev_state != STATE_SUSPEND and breakpoints_for_file is not None and line in breakpoints_for_file: breakpoint = breakpoints_for_file[line] new_frame = frame stop = True if step_cmd == CMD_STEP_OVER and stop_frame is frame and (is_line or is_return): stop = False # we don't stop on breakpoint if we have to stop by step-over (it will be processed later) elif plugin_manager is not None and main_debugger.has_plugin_line_breaks: result = plugin_manager.get_breakpoint(main_debugger, self, frame, event, self._args) if result: exist_result = True flag, breakpoint, new_frame, bp_type = result if breakpoint: # ok, hit breakpoint, now, we have to discover if it is a conditional breakpoint # lets do the conditional stuff here if stop or exist_result: eval_result = False if breakpoint.has_condition: eval_result = main_debugger.handle_breakpoint_condition(info, breakpoint, new_frame) if breakpoint.expression is not None: main_debugger.handle_breakpoint_expression(breakpoint, info, new_frame) if breakpoint.is_logpoint and info.pydev_message is not None and len(info.pydev_message) > 0: cmd = main_debugger.cmd_factory.make_io_message(info.pydev_message + os.linesep, '1') main_debugger.writer.add_command(cmd) if breakpoint.has_condition and not eval_result: return self.trace_dispatch if is_call and frame.f_code.co_name in ('<module>', '<lambda>'): # If we find a call for a module, it means that the module is being imported/executed for the # first time. In this case we have to ignore this hit as it may later duplicated by a # line event at the same place (so, if there's a module with a print() in the first line # the user will hit that line twice, which is not what we want). # # As for lambda, as it only has a single statement, it's not interesting to trace # its call and later its line event as they're usually in the same line. return self.trace_dispatch else: # if the frame is traced after breakpoint stop, # but the file should be ignored while stepping because of filters if step_cmd != -1: if main_debugger.is_filter_enabled and main_debugger.is_ignored_by_filters(filename): # ignore files matching stepping filters return self.trace_dispatch if main_debugger.is_filter_libraries and not main_debugger.in_project_scope(filename): # ignore library files while stepping return self.trace_dispatch if main_debugger.show_return_values: if is_return and info.pydev_step_cmd == CMD_STEP_OVER and frame.f_back == info.pydev_step_stop: self.show_return_values(frame, arg) elif main_debugger.remove_return_values_flag: try: self.remove_return_values(main_debugger, frame) finally: main_debugger.remove_return_values_flag = False if stop: self.set_suspend( thread, CMD_SET_BREAK, suspend_other_threads=breakpoint and breakpoint.suspend_policy == "ALL", ) elif flag and plugin_manager is not None: result = plugin_manager.suspend(main_debugger, thread, frame, bp_type) if result: frame = result # if thread has a suspend flag, we suspend with a busy wait if info.pydev_state == STATE_SUSPEND: self.do_wait_suspend(thread, frame, event, arg) return self.trace_dispatch else: if not breakpoint and is_line: # No stop from anyone and no breakpoint found in line (cache that). frame_skips_cache[line_cache_key] = 0 except: traceback.print_exc() raise # step handling. We stop when we hit the right frame try: should_skip = 0 if pydevd_dont_trace.should_trace_hook is not None: if self.should_skip == -1: # I.e.: cache the result on self.should_skip (no need to evaluate the same frame multiple times). # Note that on a code reload, we won't re-evaluate this because in practice, the frame.f_code # Which will be handled by this frame is read-only, so, we can cache it safely. if not pydevd_dont_trace.should_trace_hook(frame, filename): # -1, 0, 1 to be Cython-friendly should_skip = self.should_skip = 1 else: should_skip = self.should_skip = 0 else: should_skip = self.should_skip plugin_stop = False if should_skip: stop = False elif step_cmd == CMD_STEP_INTO: stop = is_line or is_return if plugin_manager is not None: result = plugin_manager.cmd_step_into(main_debugger, frame, event, self._args, stop_info, stop) if result: stop, plugin_stop = result elif step_cmd == CMD_STEP_INTO_MY_CODE: if main_debugger.in_project_scope(frame.f_code.co_filename): stop = is_line elif step_cmd == CMD_STEP_OVER: stop = stop_frame is frame and (is_line or is_return) if frame.f_code.co_flags & CO_GENERATOR: if is_return: stop = False if plugin_manager is not None: result = plugin_manager.cmd_step_over(main_debugger, frame, event, self._args, stop_info, stop) if result: stop, plugin_stop = result elif step_cmd == CMD_SMART_STEP_INTO: stop = False if info.pydev_smart_step_stop is frame: info.pydev_func_name = '.invalid.' # Must match the type in cython info.pydev_smart_step_stop = None if is_line or is_exception_event: curr_func_name = frame.f_code.co_name # global context is set with an empty name if curr_func_name in ('?', '<module>') or curr_func_name is None: curr_func_name = '' if curr_func_name == info.pydev_func_name: stop = True elif step_cmd == CMD_STEP_RETURN: stop = is_return and stop_frame is frame else: stop = False if stop and step_cmd != -1 and is_return and IS_PY3K and hasattr(frame, "f_back"): f_code = getattr(frame.f_back, 'f_code', None) if f_code is not None: if main_debugger.get_file_type( get_abs_path_real_path_and_base_from_file(f_code.co_filename)) == main_debugger.PYDEV_FILE: stop = False if plugin_stop: stopped_on_plugin = plugin_manager.stop(main_debugger, frame, event, self._args, stop_info, arg, step_cmd) elif stop: if is_line: self.set_suspend(thread, step_cmd) self.do_wait_suspend(thread, frame, event, arg) else: # return event back = frame.f_back if back is not None: # When we get to the pydevd run function, the debugging has actually finished for the main thread # (note that it can still go on for other threads, but for this one, we just make it finish) # So, just setting it to None should be OK _, back_filename, base = get_abs_path_real_path_and_base_from_frame(back) if (base, back.f_code.co_name) in (DEBUG_START, DEBUG_START_PY3K): back = None elif base == TRACE_PROPERTY: # We dont want to trace the return event of pydevd_traceproperty (custom property for debugging) # if we're in a return, we want it to appear to the user in the previous frame! return None if is_call else NO_FTRACE elif pydevd_dont_trace.should_trace_hook is not None: if not pydevd_dont_trace.should_trace_hook(back, back_filename): # In this case, we'll have to skip the previous one because it shouldn't be traced. # Also, we have to reset the tracing, because if the parent's parent (or some # other parent) has to be traced and it's not currently, we wouldn't stop where # we should anymore (so, a step in/over/return may not stop anywhere if no parent is traced). # Related test: _debugger_case17a.py main_debugger.set_trace_for_frame_and_parents(back) return None if is_call else NO_FTRACE if back is not None: # if we're in a return, we want it to appear to the user in the previous frame! self.set_suspend(thread, step_cmd) self.do_wait_suspend(thread, back, event, arg) else: # in jython we may not have a back frame info.pydev_step_stop = None info.pydev_step_cmd = -1 info.pydev_state = STATE_RUN except KeyboardInterrupt: raise except: try: traceback.print_exc() info.pydev_step_cmd = -1 except: return None if is_call else NO_FTRACE # if we are quitting, let's stop the tracing if not main_debugger.quitting: return self.trace_dispatch else: return None if is_call else NO_FTRACE finally: info.is_tracing = False
from ptvsd.socket import TimeoutError # noqa WAIT_FOR_THREAD_FINISH_TIMEOUT = 1 # seconds debugger_attached = threading.Event() def NOOP(*args, **kwargs): pass def path_to_unicode(s): return s if isinstance(s, unicode) else s.decode(sys.getfilesystemencoding()) PTVSD_DIR_PATH = os.path.dirname(os.path.abspath(get_abs_path_real_path_and_base_from_file(__file__)[0])) + os.path.sep NORM_PTVSD_DIR_PATH = os.path.normcase(PTVSD_DIR_PATH) class UnsupportedPyDevdCommandError(Exception): def __init__(self, cmdid): msg = 'unsupported pydevd command ' + str(cmdid) super(UnsupportedPyDevdCommandError, self).__init__(msg) self.cmdid = cmdid if sys.version_info >= (3,): def unquote(s): return None if s is None else urllib.unquote(s)
debugger_attached = threading.Event() def NOOP(*args, **kwargs): pass def path_to_unicode(s): return s if isinstance(s, unicode) else s.decode( sys.getfilesystemencoding()) PTVSD_DIR_PATH = os.path.dirname( os.path.abspath( get_abs_path_real_path_and_base_from_file(__file__)[0])) + os.path.sep NORM_PTVSD_DIR_PATH = os.path.normcase(PTVSD_DIR_PATH) class UnsupportedPyDevdCommandError(Exception): def __init__(self, cmdid): msg = 'unsupported pydevd command ' + str(cmdid) super(UnsupportedPyDevdCommandError, self).__init__(msg) self.cmdid = cmdid if sys.version_info >= (3, ): def unquote(s): return None if s is None else urllib.unquote(s)