import linecache import os.path import re import sys import traceback # @Reimport # IFDEF CYTHON # import dis # ENDIF from _pydev_bundle import pydev_log from _pydevd_bundle import pydevd_dont_trace from _pydevd_bundle import pydevd_vars from _pydevd_bundle.pydevd_breakpoints import get_exception_breakpoint from _pydevd_bundle.pydevd_comm_constants import (CMD_STEP_CAUGHT_EXCEPTION, CMD_STEP_RETURN, CMD_STEP_OVER, CMD_SET_BREAK, CMD_STEP_INTO, CMD_SMART_STEP_INTO, CMD_STEP_INTO_MY_CODE, CMD_STEP_INTO_COROUTINE) from _pydevd_bundle.pydevd_constants import STATE_SUSPEND, get_current_thread_id, STATE_RUN, dict_iter_values, IS_PY3K, \ dict_keys, RETURN_VALUES_DICT, NO_FTRACE, IS_CPYTHON from _pydevd_bundle.pydevd_dont_trace_files import DONT_TRACE, PYDEV_FILE from _pydevd_bundle.pydevd_frame_utils import add_exception_to_frame, just_raised, remove_exception_from_frame, ignore_exception_trace from _pydevd_bundle.pydevd_bytecode_utils import find_last_call_name, find_last_func_call_order from _pydevd_bundle.pydevd_utils import get_clsname_for_code, should_stop_on_failed_test, is_exception_in_test_unit_can_be_ignored, eval_expression from pydevd_file_utils import get_abs_path_real_path_and_base_from_frame try: from inspect import CO_GENERATOR except: CO_GENERATOR = 0 from _pydevd_bundle.pydevd_constants import IS_PY2 try: from _pydevd_bundle.pydevd_signature import send_signature_call_trace, send_signature_return_trace except ImportError: def send_signature_call_trace(*args, **kwargs): pass basename = os.path.basename IGNORE_EXCEPTION_TAG = re.compile('[^#]*#.*@IgnoreException') DEBUG_START = ('pydevd.py', '_exec') DEBUG_START_PY3K = ('_pydev_execfile.py', 'execfile') TRACE_PROPERTY = 'pydevd_traceproperty.py' get_file_type = DONT_TRACE.get def handle_breakpoint_condition(py_db, info, breakpoint, new_frame): condition = breakpoint.condition try: if breakpoint.handle_hit_condition(new_frame): return True if condition is None: return False return eval_expression(condition, new_frame.f_globals, new_frame.f_locals) except Exception as e: if IS_PY2: # Must be bytes on py2. if isinstance(condition, unicode): condition = condition.encode('utf-8') if not isinstance(e, py_db.skip_print_breakpoint_exception): sys.stderr.write('Error while evaluating expression: %s\n' % (condition,)) etype, value, tb = sys.exc_info() traceback.print_exception(etype, value, tb.tb_next) if not isinstance(e, py_db.skip_suspend_on_breakpoint_exception): try: # add exception_type and stacktrace into thread additional info etype, value, tb = sys.exc_info() error = ''.join(traceback.format_exception_only(etype, value)) stack = traceback.extract_stack(f=tb.tb_frame.f_back) # On self.set_suspend(thread, CMD_SET_BREAK) this info will be # sent to the client. info.conditional_breakpoint_exception = \ ('Condition:\n' + condition + '\n\nError:\n' + error, stack) except: traceback.print_exc() return True return False finally: etype, value, tb = None, None, None def handle_breakpoint_expression(breakpoint, info, new_frame): try: try: val = eval_expression(breakpoint.expression, new_frame.f_globals, new_frame.f_locals) except: val = sys.exc_info()[1] finally: if val is not None: info.pydev_message = str(val) # IFDEF CYTHON # def should_stop_on_exception(tuple args, frame, str event, tuple arg): # cdef PyDBAdditionalThreadInfo info; # cdef bint flag; # ELSE def should_stop_on_exception(args, frame, event, arg): # ENDIF # main_debugger, _filename, info, _thread = args main_debugger = args[0] info = args[2] should_stop = False # STATE_SUSPEND = 2 if info.pydev_state != 2: # and breakpoint is not None: exception, value, trace = arg if trace is not None and hasattr(trace, 'tb_next'): # on jython trace is None on the first event and it may not have a tb_next. exception_breakpoint = get_exception_breakpoint( exception, main_debugger.break_on_caught_exceptions) if exception_breakpoint is not None: if exception_breakpoint.condition is not None: # Always add exception to frame (must remove later after we proceed). add_exception_to_frame(frame, (exception, value, trace)) eval_result = handle_breakpoint_condition(main_debugger, info, exception_breakpoint, frame) remove_exception_from_frame(frame) if not eval_result: return False, frame if exception_breakpoint.ignore_libraries: if not main_debugger.is_exception_trace_in_project_scope(trace): return False, frame if ignore_exception_trace(trace): return False, frame was_just_raised = just_raised(trace) if was_just_raised: if main_debugger.skip_on_exceptions_thrown_in_same_context: # Option: Don't break if an exception is caught in the same function from which it is thrown return False, frame if exception_breakpoint.notify_on_first_raise_only: if main_debugger.skip_on_exceptions_thrown_in_same_context: # In this case we never stop if it was just raised, so, to know if it was the first we # need to check if we're in the 2nd method. if not was_just_raised and not just_raised(trace.tb_next): return False, frame # I.e.: we stop only when we're at the caller of a method that throws an exception else: if not was_just_raised and not main_debugger.is_top_level_trace_in_project_scope(trace): return False, frame # I.e.: we stop only when it was just raised # If it got here we should stop. should_stop = True try: info.pydev_message = exception_breakpoint.qname except: info.pydev_message = exception_breakpoint.qname.encode('utf-8') # Always add exception to frame (must remove later after we proceed). add_exception_to_frame(frame, (exception, value, trace)) info.pydev_message = "python-%s" % info.pydev_message else: # No regular exception breakpoint, let's see if some plugin handles it or if it is a test assertion error. try: if main_debugger.plugin is not None: result = main_debugger.plugin.exception_break(main_debugger, frame, args, arg) if result: should_stop, frame = result if main_debugger.stop_on_failed_tests and main_debugger.is_test_item_or_set_up_caller(trace) \ and not is_exception_in_test_unit_can_be_ignored(exception): should_stop, frame = should_stop_on_failed_test(arg), frame info.pydev_message = "python-AssertionError" except: should_stop = False if should_stop: if exception_breakpoint is not None and exception_breakpoint.expression is not None: handle_breakpoint_expression(exception_breakpoint, info, frame) return should_stop, frame # Same thing in the main debugger but only considering the file contents, # while the one in the main debugger considers the user input (so, the actual result # must be a join of both). filename_to_lines_where_exceptions_are_ignored = {} filename_to_stat_info = {} # IFDEF CYTHON # def handle_exception(tuple args, frame, str event, tuple arg): # ELSE def handle_exception(args, frame, event, arg): # ENDIF try: # We have 3 things in arg: exception type, description, traceback object trace_obj = arg[2] main_debugger = args[0] initial_trace_obj = trace_obj if trace_obj.tb_next is None and trace_obj.tb_frame is frame: # I.e.: tb_next should be only None in the context it was thrown (trace_obj.tb_frame is frame is just a double check). pass else: # Get the trace_obj from where the exception was raised... while trace_obj.tb_next is not None: trace_obj = trace_obj.tb_next if main_debugger.ignore_exceptions_thrown_in_lines_with_ignore_exception \ and not main_debugger.stop_on_failed_tests: for check_trace_obj in (initial_trace_obj, trace_obj): filename = get_abs_path_real_path_and_base_from_frame(check_trace_obj.tb_frame)[1] lines_ignored = filename_to_lines_where_exceptions_are_ignored.get(filename) if lines_ignored is None: lines_ignored = filename_to_lines_where_exceptions_are_ignored[filename] = {} try: curr_stat = os.stat(filename) curr_stat = (curr_stat.st_size, curr_stat.st_mtime) except: curr_stat = None last_stat = filename_to_stat_info.get(filename) if last_stat != curr_stat: filename_to_stat_info[filename] = curr_stat lines_ignored.clear() try: linecache.checkcache(filename) except: # Jython 2.1 linecache.checkcache() from_user_input = main_debugger.filename_to_lines_where_exceptions_are_ignored.get(filename) if from_user_input: merged = {} merged.update(lines_ignored) # Override what we have with the related entries that the user entered merged.update(from_user_input) else: merged = lines_ignored exc_lineno = check_trace_obj.tb_lineno # print ('lines ignored', lines_ignored) # print ('user input', from_user_input) # print ('merged', merged, 'curr', exc_lineno) if exc_lineno not in merged: # Note: check on merged but update lines_ignored. try: line = linecache.getline(filename, exc_lineno, check_trace_obj.tb_frame.f_globals) except: # Jython 2.1 line = linecache.getline(filename, exc_lineno) if IGNORE_EXCEPTION_TAG.match(line) is not None: lines_ignored[exc_lineno] = 1 return else: # Put in the cache saying not to ignore lines_ignored[exc_lineno] = 0 else: # Ok, dict has it already cached, so, let's check it... if merged.get(exc_lineno, 0): return thread = args[3] try: frame_id_to_frame = {} frame_id_to_frame[id(frame)] = frame f = trace_obj.tb_frame while f is not None: frame_id_to_frame[id(f)] = f f = f.f_back f = None thread_id = get_current_thread_id(thread) if main_debugger.stop_on_failed_tests: # Our goal is to find the deepest frame in stack that still belongs to the project and stop there. f = trace_obj.tb_frame while f: abs_path, _, _ = get_abs_path_real_path_and_base_from_frame(f) if main_debugger.in_project_scope(abs_path): frame = f break f = f.f_back f = None trace_obj = initial_trace_obj while trace_obj: if trace_obj.tb_frame is frame: break trace_obj = trace_obj.tb_next add_exception_to_frame(frame, (arg[0], arg[1], trace_obj)) pydevd_vars.add_additional_frame_by_id(thread_id, frame_id_to_frame) try: main_debugger.send_caught_exception_stack(thread, arg, id(frame)) main_debugger.set_suspend(thread, CMD_STEP_CAUGHT_EXCEPTION) main_debugger.do_wait_suspend(thread, frame, event, arg) main_debugger.send_caught_exception_stack_proceeded(thread) finally: pydevd_vars.remove_additional_frame_by_id(thread_id) except KeyboardInterrupt as e: raise e except: traceback.print_exc() main_debugger.set_trace_for_frame_and_parents(frame) finally: # Make sure the user cannot see the '__exception__' we added after we leave the suspend state. remove_exception_from_frame(frame) # Clear some local variables... frame = None trace_obj = None initial_trace_obj = None check_trace_obj = None f = None frame_id_to_frame = None main_debugger = None thread = None def manage_return_values(main_debugger, frame, event, arg): def get_func_name(frame): code_obj = frame.f_code func_name = code_obj.co_name try: cls_name = get_clsname_for_code(code_obj, frame) if cls_name is not None: return "%s.%s" % (cls_name, func_name) else: return func_name except: traceback.print_exc() return func_name try: if main_debugger.show_return_values: if event == "return" and hasattr(frame, "f_code") and hasattr(frame.f_code, "co_name"): if hasattr(frame, "f_back") and hasattr(frame.f_back, "f_locals"): if RETURN_VALUES_DICT not in dict_keys(frame.f_back.f_locals): frame.f_back.f_locals[RETURN_VALUES_DICT] = {} name = get_func_name(frame) frame.f_back.f_locals[RETURN_VALUES_DICT][name] = arg if main_debugger.remove_return_values_flag: # Showing return values was turned off, we should remove them from locals dict. # The values can be in the current frame or in the back one if RETURN_VALUES_DICT in dict_keys(frame.f_locals): frame.f_locals.pop(RETURN_VALUES_DICT) if hasattr(frame, "f_back") and hasattr(frame.f_back, "f_locals"): if RETURN_VALUES_DICT in dict_keys(frame.f_back.f_locals): frame.f_back.f_locals.pop(RETURN_VALUES_DICT) main_debugger.remove_return_values_flag = False except: main_debugger.remove_return_values_flag = False traceback.print_exc() #======================================================================================================================= # PyDBFrame #======================================================================================================================= # IFDEF CYTHON # cdef class PyDBFrame: # ELSE class PyDBFrame: '''This makes the tracing for a given frame, so, the trace_dispatch is used initially when we enter into a new context ('call') and then is reused for the entire context. ''' # ENDIF # Note: class (and not instance) attributes. # IFDEF CYTHON # cdef tuple _args # cdef int should_skip # cdef int _bytecode_offset # cdef list _instructions # def __init__(self, tuple args): # self._args = args # In the cython version we don't need to pass the frame # self.should_skip = -1 # On cythonized version, put in instance. # self._bytecode_offset = 0 # self._instructions = None # ELSE should_skip = -1 # Default value in class (put in instance on set). def __init__(self, args): # args = main_debugger, filename, base, info, t, frame # yeap, much faster than putting in self and then getting it from self later on self._args = args self._bytecode_offset = 0 # ENDIF def set_suspend(self, *args, **kwargs): self._args[0].set_suspend(*args, **kwargs) def do_wait_suspend(self, *args, **kwargs): self._args[0].do_wait_suspend(*args, **kwargs) # IFDEF CYTHON # def trace_exception(self, frame, str event, arg): # cdef bint should_stop; # ELSE def trace_exception(self, frame, event, arg): # ENDIF if event == 'exception': should_stop, frame = should_stop_on_exception(self._args, frame, event, arg) if should_stop: handle_exception(self._args, frame, event, arg) return self.trace_dispatch return self.trace_exception def trace_return(self, frame, event, arg): if event == 'return': main_debugger, filename = self._args[0], self._args[1] send_signature_return_trace(main_debugger, frame, filename, arg) return self.trace_return def get_func_name(self, frame): code_obj = frame.f_code func_name = code_obj.co_name try: cls_name = get_clsname_for_code(code_obj, frame) if cls_name is not None: return "%s.%s" % (cls_name, func_name) else: return func_name except: traceback.print_exc() return func_name def clear_run_state(self, info): info.pydev_step_stop = None info.pydev_step_cmd = -1 info.pydev_state = STATE_RUN # IFDEF CYTHON # cpdef trace_dispatch(self, frame, str event, arg): # cdef str filename; # cdef bint is_exception_event; # cdef bint has_exception_breakpoints; # cdef bint can_skip; # cdef PyDBAdditionalThreadInfo info; # cdef int step_cmd; # cdef int line; # cdef bint is_line; # cdef bint is_call; # cdef bint is_return; # cdef bint should_stop; # cdef dict breakpoints_for_file; # cdef str curr_func_name; # cdef bint exist_result; # cdef dict frame_skips_cache; # cdef tuple frame_cache_key; # cdef tuple line_cache_key; # cdef int breakpoints_in_line_cache; # cdef int breakpoints_in_frame_cache; # cdef bint has_breakpoint_in_frame; # cdef bint need_trace_return; # ELSE def trace_dispatch(self, frame, event, arg): # ENDIF main_debugger, filename, info, thread, frame_skips_cache, frame_cache_key = self._args # 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)) # The thread can be already suspended by another function, e.g. built-in breakpoint hook. if info.is_tracing: return None try: info.is_tracing = True line = frame.f_lineno line_cache_key = (frame_cache_key, line) if main_debugger._finish_debugging_session: if event != 'call': frame.f_trace = NO_FTRACE return None # IFDEF CYTHON # if event == 'opcode': # instructions = self._get_instructions(frame) # for i, inst in enumerate(instructions): # if inst.offset == frame.f_lasti: # opname, arg, argval = inst.opname, inst.arg, str(inst.argval) # print('frame trace_dispatch %s %s %s %s %s %s %s %s' % (frame.f_lineno, frame.f_lasti, frame.f_code.co_name, # frame.f_code.co_filename, event, opname, arg, argval)) # try: # self._bytecode_offset = instructions[i + 1].offset # except IndexError: # break # return self.trace_dispatch # ENDIF 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 \ or main_debugger.stop_on_failed_tests if is_exception_event: if has_exception_breakpoints: should_stop, frame = should_stop_on_exception( self._args, frame, event, arg) if should_stop: handle_exception(self._args, frame, event, arg) # No need to reset frame.f_trace to keep the same trace function. 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. # No need to reset frame.f_trace to keep the same trace function. 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 is_generator_or_coroutime = frame.f_code.co_flags & 0xa0 # 0xa0 == CO_GENERATOR = 0x20 | CO_COROUTINE = 0x80 breakpoints_for_file = main_debugger.breakpoints.get(filename) if not is_exception_event: if is_generator_or_coroutime: if is_return: # Dealing with coroutines and generators: # When in a coroutine we change the perceived event to the debugger because # a call, StopIteration exception and return are usually just pausing/unpausing it. returns_cache_key = (frame_cache_key, 'returns') return_lines = frame_skips_cache.get(returns_cache_key) if return_lines is None: # Note: we're collecting the return lines by inspecting the bytecode as # there are multiple returns and multiple stop iterations when awaiting and # it doesn't give any clear indication when a coroutine or generator is # finishing or just pausing. return_lines = set() for x in main_debugger.collect_return_info(frame.f_code): # Note: cython does not support closures in cpdefs (so we can't use # a list comprehension). return_lines.add(x.return_line) frame_skips_cache[returns_cache_key] = return_lines if line not in return_lines: # Not really a return (coroutine/generator paused). return self.trace_dispatch elif is_call: # Don't stop when calling coroutines, we will on other event anyway if necessary. return self.trace_dispatch 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, frame, info) # 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: frame.f_trace = self.trace_exception return self.trace_exception else: if need_signature_trace_return: frame.f_trace = self.trace_return return self.trace_return else: if not is_call: frame.f_trace = NO_FTRACE return None 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: # No need to reset frame.f_trace to keep the same trace function. 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 ('?', '', ''): 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: frame.f_trace = self.trace_exception return self.trace_exception else: if need_signature_trace_return: frame.f_trace = self.trace_return return self.trace_return else: if not is_call: frame.f_trace = NO_FTRACE return None # 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 # 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 smart_stop_frame = info.pydev_smart_step_context.smart_step_stop context_start_line = info.pydev_smart_step_context.start_line context_end_line = info.pydev_smart_step_context.end_line is_within_context = context_start_line <= line <= context_end_line if not is_return and info.pydev_state != STATE_SUSPEND and breakpoints_for_file is not None and line in breakpoints_for_file and not is_exception_event: breakpoint = breakpoints_for_file[line] new_frame = frame stop = True if step_cmd == CMD_STEP_OVER: if 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 is_generator_or_coroutime and frame.f_back and frame.f_back is stop_frame: stop = False # we don't stop on breakpoint if stepping is active and we enter a `genexpr` or coroutine context elif step_cmd == CMD_SMART_STEP_INTO and (frame.f_back is smart_stop_frame and is_within_context): stop = False elif plugin_manager is not None and main_debugger.has_plugin_line_breaks: result = plugin_manager.get_breakpoint(main_debugger, 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 = handle_breakpoint_condition(main_debugger, info, breakpoint, new_frame) if breakpoint.expression is not None: 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: # No need to reset frame.f_trace to keep the same trace function. return self.trace_dispatch if is_call and frame.f_code.co_name in ('', ''): # 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. # No need to reset frame.f_trace to keep the same trace function. 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 # No need to reset frame.f_trace to keep the same trace function. return self.trace_dispatch if main_debugger.is_filter_libraries and not main_debugger.in_project_scope(filename): # ignore library files while stepping # No need to reset frame.f_trace to keep the same trace function. return self.trace_dispatch if main_debugger.show_return_values or main_debugger.remove_return_values_flag: manage_return_values(main_debugger, frame, event, arg) 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) # No need to reset frame.f_trace to keep the same trace function. 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 KeyboardInterrupt: self.clear_run_state(info) raise 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_SMART_STEP_INTO: stop = False if smart_stop_frame is frame: if not is_within_context or not IS_CPYTHON: # We don't stop on jumps in multiline statements, which the Python interpreter does in some cases, # if we they happen in smart step into context. info.pydev_func_name = '.invalid.' # Must match the type in cython stop = True # act as if we did a step into 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 ('?', '') or curr_func_name is None: curr_func_name = '' if smart_stop_frame and smart_stop_frame is frame.f_back: if curr_func_name == info.pydev_func_name and not IS_CPYTHON: # for implementations other than CPython we don't perform any additional checks stop = True else: try: if curr_func_name != info.pydev_func_name and frame.f_back: # try to find function call name using bytecode analysis curr_func_name = find_last_call_name(frame.f_back) if curr_func_name == info.pydev_func_name: stop = find_last_func_call_order(frame.f_back, context_start_line) \ == info.pydev_smart_step_context.call_order except: pydev_log.debug("Exception while handling smart step into in frame tracer, step into will be performed instead.") info.pydev_smart_step_context.reset() stop = True # act as if we did a step into # we have to check this case for situations when a user has tried to step into a native function or method, # e.g. `len()`, `list.append()`, etc and this was the only call in a return statement if smart_stop_frame is frame and is_return: stop = True 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 in (CMD_STEP_OVER, CMD_STEP_INTO_COROUTINE): stop = stop_frame is frame if stop: if is_line: # the only case we shouldn't stop on a line, is when we traversing though asynchronous framework machinery if step_cmd == CMD_STEP_INTO_COROUTINE: stop = main_debugger.in_project_scope(frame.f_code.co_filename) elif is_return: stop = frame.f_back and main_debugger.in_project_scope(frame.f_back.f_code.co_filename) if not stop: back = frame.f_back if back: info.pydev_step_stop = back if main_debugger.in_project_scope(frame.f_code.co_filename): # we are returning from the project scope, step over should always lead to the project scope if is_generator_or_coroutime and step_cmd == CMD_STEP_OVER: # setting ad hoc command to ensure we will skip line stops in an asynchronous framework info.pydev_step_cmd = CMD_STEP_INTO_COROUTINE else: # we were already outside the project scope because of step into or breakpoint, it's ok to stop # if we are not chopping a way through an asynchronous framework stop = not step_cmd == CMD_STEP_INTO_COROUTINE else: # if there's no back frame, we just stop as soon as possible info.pydev_step_cmd = CMD_STEP_INTO info.pydev_step_stop = None else: stop = False if CMD_STEP_OVER and 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_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: back_filename = os.path.basename(f_code.co_filename) file_type = get_file_type(back_filename) if file_type == 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! if not is_call: frame.f_trace = NO_FTRACE return None 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) if not is_call: frame.f_trace = NO_FTRACE return None 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 self.clear_run_state(info) except KeyboardInterrupt: self.clear_run_state(info) raise except: try: traceback.print_exc() info.pydev_step_cmd = -1 except: if not is_call: frame.f_trace = NO_FTRACE return None # if we are quitting, let's stop the tracing if not main_debugger.quitting: # No need to reset frame.f_trace to keep the same trace function. return self.trace_dispatch else: if not is_call: frame.f_trace = NO_FTRACE return None finally: info.is_tracing = False # end trace_dispatch # IFDEF CYTHON # cdef _get_instructions(self, frame): # if self._instructions is None: # self._instructions = list(dis.get_instructions(frame.f_code)) # return self._instructions # ENDIF