def _pydev_stop_at_break():
    frame = sys._getframe(1)
    t = threading.currentThread()
    if t.additional_info.is_tracing:
        return False

    if t.additional_info.pydev_step_cmd == -1 and frame.f_trace in (None, dummy_tracing_holder.dummy_trace_func):
        # do not handle breakpoints while stepping, because they're handled by old tracing function
        t.additional_info.is_tracing = True
        debugger = get_global_debugger()

        try:
            abs_path_real_path_and_base = NORM_PATHS_AND_BASE_CONTAINER[frame.f_code.co_filename]
        except:
            abs_path_real_path_and_base = get_abs_path_real_path_and_base_from_frame(frame)
        filename = abs_path_real_path_and_base[1]

        breakpoints_for_file = debugger.breakpoints.get(filename)
        line = _get_line_for_frame(frame)
        try:
            breakpoint = breakpoints_for_file[line]
        except KeyError:
            pydev_log.debug("Couldn't find breakpoint in the file {} on line {}".format(frame.f_code.co_filename, line))
            t.additional_info.is_tracing = False
            return False
        if breakpoint and handle_breakpoint(frame, t, debugger, breakpoint):
            pydev_log.debug("Suspending at breakpoint in file: {} on line {}".format(frame.f_code.co_filename, line))
            debugger.set_suspend(t, CMD_SET_BREAK)
            debugger.do_wait_suspend(t, frame, 'line', None, "frame_eval")
        t.additional_info.is_tracing = False
        return t.additional_info.pydev_step_cmd == CMD_SET_NEXT_STATEMENT
    return False
예제 #2
0
def break_into_debugger():
    """If a remote debugger is attached, pauses execution of all threads,
    and breaks into the debugger with current thread as active.
    """

    ptvsd.log.info('break_into_debugger()')

    if not is_attached():
        ptvsd.log.info('break_into_debugger() ignored - debugger not attached')
        return

    # Get the first frame in the stack that's not an internal frame.
    global_debugger = get_global_debugger()
    stop_at_frame = sys._getframe().f_back
    while stop_at_frame is not None and global_debugger.get_file_type(
            get_abs_path_real_path_and_base_from_frame(stop_at_frame)) == global_debugger.PYDEV_FILE:
        stop_at_frame = stop_at_frame.f_back

    _pydevd_settrace(
        suspend=True,
        trace_only_current_thread=True,
        patch_multiprocessing=False,
        stop_at_frame=stop_at_frame,
    )
    stop_at_frame = None
예제 #3
0
    def _schedule_callback(prev, next):
        '''
        Called when a context is stopped or a new context is made runnable.
        '''
        try:
            if not prev and not next:
                return

            if next:
                register_tasklet_info(next)

                # Ok, making next runnable: set the tracing facility in it.
                debugger = get_global_debugger()
                if debugger is not None and next.frame:
                    if hasattr(next.frame, 'f_trace'):
                        next.frame.f_trace = debugger.trace_dispatch
                debugger = None

            if prev:
                register_tasklet_info(prev)

            try:
                for tasklet_ref, tasklet_info in dict_items(_weak_tasklet_registered_to_info):  # Make sure it's a copy!
                    tasklet = tasklet_ref()
                    if tasklet is None or not tasklet.alive:
                        # Garbage-collected already!
                        try:
                            del _weak_tasklet_registered_to_info[tasklet_ref]
                        except KeyError:
                            pass
                        if tasklet_info.frame_id is not None:
                            remove_custom_frame(tasklet_info.frame_id)
                    else:
                        if tasklet.paused or tasklet.blocked or tasklet.scheduled:
                            if tasklet.frame and tasklet.frame.f_back:
                                f_back = tasklet.frame.f_back
                                base = get_abs_path_real_path_and_base_from_frame(f_back)[-1]
                                is_file_to_ignore = dict_contains(DONT_TRACE, base)
                                if not is_file_to_ignore:
                                    if tasklet_info.frame_id is None:
                                        tasklet_info.frame_id = add_custom_frame(f_back, tasklet_info.tasklet_name, tasklet.thread_id)
                                    else:
                                        update_custom_frame(tasklet_info.frame_id, f_back, tasklet.thread_id)

                        elif tasklet.is_current:
                            if tasklet_info.frame_id is not None:
                                # Remove info about stackless suspended when it starts to run.
                                remove_custom_frame(tasklet_info.frame_id)
                                tasklet_info.frame_id = None

            finally:
                tasklet = None
                tasklet_info = None
                f_back = None

        except:
            import traceback;traceback.print_exc()

        if _application_set_schedule_callback is not None:
            return _application_set_schedule_callback(prev, next)
예제 #4
0
def add_custom_frame(frame, name, thread_id):
    '''
    It's possible to show paused frames by adding a custom frame through this API (it's
    intended to be used for coroutines, but could potentially be used for generators too).

    :param frame:
        The topmost frame to be shown paused when a thread with thread.ident == thread_id is paused.

    :param name:
        The name to be shown for the custom thread in the UI.

    :param thread_id:
        The thread id to which this frame is related (must match thread.ident).

    :return: str
        Returns the custom thread id which will be used to show the given frame paused.
    '''
    with CustomFramesContainer.custom_frames_lock:
        curr_thread_id = get_current_thread_id(threading.currentThread())
        next_id = CustomFramesContainer._next_frame_id = CustomFramesContainer._next_frame_id + 1

        # Note: the frame id kept contains an id and thread information on the thread where the frame was added
        # so that later on we can check if the frame is from the current thread by doing frame_id.endswith('|'+thread_id).
        frame_custom_thread_id = '__frame__:%s|%s' % (next_id, curr_thread_id)
        if DEBUG:
            sys.stderr.write('add_custom_frame: %s (%s) %s %s\n' % (
                frame_custom_thread_id, get_abs_path_real_path_and_base_from_frame(frame)[-1], frame.f_lineno, frame.f_code.co_name))

        CustomFramesContainer.custom_frames[frame_custom_thread_id] = CustomFrame(name, frame, thread_id)
        CustomFramesContainer._py_db_command_thread_event.set()
        return frame_custom_thread_id
    def make_thread_stack_str(self, frame, frame_id_to_lineno=None):
        '''
        :param frame_id_to_lineno:
            If available, the line number for the frame will be gotten from this dict,
            otherwise frame.f_lineno will be used (needed for unhandled exceptions as
            the place where we report may be different from the place where it's raised).
        '''
        if frame_id_to_lineno is None:
            frame_id_to_lineno = {}
        make_valid_xml_value = pydevd_xml.make_valid_xml_value
        cmd_text_list = []
        append = cmd_text_list.append

        curr_frame = frame
        frame = None  # Clear frame reference
        try:
            py_db = get_global_debugger()
            while curr_frame:
                frame_id = id(curr_frame)

                if curr_frame.f_code is None:
                    break  # Iron Python sometimes does not have it!

                method_name = curr_frame.f_code.co_name  # method name (if in method) or ? if global
                if method_name is None:
                    break  # Iron Python sometimes does not have it!

                abs_path_real_path_and_base = get_abs_path_real_path_and_base_from_frame(curr_frame)
                if py_db.get_file_type(abs_path_real_path_and_base) == py_db.PYDEV_FILE:
                    # Skip pydevd files.
                    curr_frame = curr_frame.f_back
                    continue

                filename_in_utf8 = pydevd_file_utils.norm_file_to_client(abs_path_real_path_and_base[0])
                if not filesystem_encoding_is_utf8 and hasattr(filename_in_utf8, "decode"):
                    # filename_in_utf8 is a byte string encoded using the file system encoding
                    # convert it to utf8
                    filename_in_utf8 = filename_in_utf8.decode(file_system_encoding).encode("utf-8")

                # print("file is ", filename_in_utf8)

                lineno = frame_id_to_lineno.get(frame_id, curr_frame.f_lineno)
                # print("line is ", lineno)

                # Note: variables are all gotten 'on-demand'.
                append('<frame id="%s" name="%s" ' % (frame_id , make_valid_xml_value(method_name)))
                append('file="%s" line="%s">' % (quote(make_valid_xml_value(filename_in_utf8), '/>_= \t'), lineno))
                append("</frame>")
                curr_frame = curr_frame.f_back
        except:
            traceback.print_exc()

        curr_frame = None  # Clear frame reference
        return ''.join(cmd_text_list)
def get_text_list_for_frame(frame):
    # partial copy-paste from make_thread_suspend_str
    curFrame = frame
    cmdTextList = []
    try:
        while curFrame:
            #print cmdText
            myId = str(id(curFrame))
            #print "id is ", myId

            if curFrame.f_code is None:
                break #Iron Python sometimes does not have it!

            myName = curFrame.f_code.co_name #method name (if in method) or ? if global
            if myName is None:
                break #Iron Python sometimes does not have it!

            #print "name is ", myName

            filename = pydevd_file_utils.get_abs_path_real_path_and_base_from_frame(curFrame)[1]

            myFile = pydevd_file_utils.norm_file_to_client(filename)
            if file_system_encoding.lower() != "utf-8" and hasattr(myFile, "decode"):
                # myFile is a byte string encoded using the file system encoding
                # convert it to utf8
                myFile = myFile.decode(file_system_encoding).encode("utf-8")

            #print "file is ", myFile
            #myFile = inspect.getsourcefile(curFrame) or inspect.getfile(frame)

            myLine = str(curFrame.f_lineno)
            #print "line is ", myLine

            #the variables are all gotten 'on-demand'
            #variables = pydevd_xml.frame_vars_to_xml(curFrame.f_locals)

            variables = ''
            cmdTextList.append('<frame id="%s" name="%s" ' % (myId , pydevd_xml.make_valid_xml_value(myName)))
            cmdTextList.append('file="%s" line="%s">' % (quote(myFile, '/>_= \t'), myLine))
            cmdTextList.append(variables)
            cmdTextList.append("</frame>")
            curFrame = curFrame.f_back
    except :
        traceback.print_exc()

    return cmdTextList
def add_custom_frame(frame, name, thread_id):
    CustomFramesContainer.custom_frames_lock.acquire()
    try:
        curr_thread_id = get_current_thread_id(threading.currentThread())
        next_id = CustomFramesContainer._next_frame_id = CustomFramesContainer._next_frame_id + 1

        # Note: the frame id kept contains an id and thread information on the thread where the frame was added
        # so that later on we can check if the frame is from the current thread by doing frame_id.endswith('|'+thread_id).
        frame_id = '__frame__:%s|%s' % (next_id, curr_thread_id)
        if DEBUG:
            sys.stderr.write('add_custom_frame: %s (%s) %s %s\n' % (
                frame_id, get_abs_path_real_path_and_base_from_frame(frame)[-1], frame.f_lineno, frame.f_code.co_name))

        CustomFramesContainer.custom_frames[frame_id] = CustomFrame(name, frame, thread_id)
        CustomFramesContainer._py_db_command_thread_event.set()
        return frame_id
    finally:
        CustomFramesContainer.custom_frames_lock.release()
def get_text_list_for_frame(frame):
    # partial copy-paste from make_thread_suspend_str
    curFrame = frame
    cmdTextList = []
    try:
        while curFrame:
            # print cmdText
            myId = str(id(curFrame))
            # print "id is ", myId

            if curFrame.f_code is None:
                break  # Iron Python sometimes does not have it!

            myName = curFrame.f_code.co_name  # method name (if in method) or ? if global
            if myName is None:
                break  # Iron Python sometimes does not have it!

            # print "name is ", myName

            filename = pydevd_file_utils.get_abs_path_real_path_and_base_from_frame(curFrame)[1]

            myFile = pydevd_file_utils.norm_file_to_client(filename)

            # print "file is ", myFile
            # myFile = inspect.getsourcefile(curFrame) or inspect.getfile(frame)

            myLine = str(curFrame.f_lineno)
            # print "line is ", myLine

            # the variables are all gotten 'on-demand'
            # variables = pydevd_xml.frame_vars_to_xml(curFrame.f_locals)

            variables = ''
            cmdTextList.append('<frame id="%s" name="%s" ' % (myId , pydevd_xml.make_valid_xml_value(myName)))
            cmdTextList.append('file="%s" line="%s">' % (quote(myFile, '/>_= \t'), myLine))
            cmdTextList.append(variables)
            cmdTextList.append("</frame>")
            curFrame = curFrame.f_back
    except :
        pydev_log.exception()

    return cmdTextList
예제 #9
0
def _pydev_stop_at_break(line):
    frame = sys._getframe(1)
    t = threading.currentThread()
    if t.additional_info.is_tracing:
        return False

    t.additional_info.is_tracing = True
    try:
        debugger = get_global_debugger()

        try:
            abs_path_real_path_and_base = NORM_PATHS_AND_BASE_CONTAINER[frame.f_code.co_filename]
        except:
            abs_path_real_path_and_base = get_abs_path_real_path_and_base_from_frame(frame)
        filename = abs_path_real_path_and_base[1]

        try:
            python_breakpoint = debugger.breakpoints[filename][line]
        except:
            # print("Couldn't find breakpoint in the file %s on line %s" % (frame.f_code.co_filename, line))
            # Could be KeyError if line is not there or TypeError if breakpoints_for_file is None.
            # Note: using catch-all exception for performance reasons (if the user adds a breakpoint
            # and then removes it after hitting it once, this method added for the programmatic
            # breakpoint will keep on being called and one of those exceptions will always be raised
            # here).
            return

        if python_breakpoint:
            pydev_log.debug("Suspending at breakpoint in file: {} on line {}".format(frame.f_code.co_filename, line))
            t.additional_info.trace_suspend_type = 'frame_eval'

            pydevd_frame_eval_cython_wrapper = sys.modules['_pydevd_frame_eval.pydevd_frame_eval_cython_wrapper']
            thread_info = pydevd_frame_eval_cython_wrapper.get_thread_info_py()
            if thread_info.thread_trace_func is not None:
                frame.f_trace = thread_info.thread_trace_func
            else:
                debugger = get_global_debugger()
                frame.f_trace = debugger.get_thread_local_trace_func()

    finally:
        t.additional_info.is_tracing = False
    def _iter_visible_frames_info(self, py_db, frame, frame_id_to_lineno):
        while frame is not None:
            if frame.f_code is None:
                continue  # IronPython sometimes does not have it!

            method_name = frame.f_code.co_name  # method name (if in method) or ? if global
            if method_name is None:
                continue  # IronPython sometimes does not have it!

            abs_path_real_path_and_base = get_abs_path_real_path_and_base_from_frame(frame)
            if py_db.get_file_type(abs_path_real_path_and_base) == py_db.PYDEV_FILE:
                # Skip pydevd files.
                frame = frame.f_back
                continue

            filename_in_utf8 = pydevd_file_utils.norm_file_to_client(abs_path_real_path_and_base[0])

            frame_id = id(frame)
            lineno = frame_id_to_lineno.get(frame_id, frame.f_lineno)

            yield frame_id, frame, method_name, filename_in_utf8, lineno

            frame = frame.f_back
예제 #11
0
    def log_event(self, frame):
        write_log = False
        self_obj = None
        if "self" in frame.f_locals:
            self_obj = frame.f_locals["self"]
            if isinstance(
                    self_obj,
                    threading.Thread) or self_obj.__class__ == ObjectWrapper:
                write_log = True
        if hasattr(frame, "f_back") and frame.f_back is not None:
            back = frame.f_back
            if hasattr(back, "f_back") and back.f_back is not None:
                back = back.f_back
                if "self" in back.f_locals:
                    if isinstance(back.f_locals["self"], threading.Thread):
                        write_log = True
        try:
            if write_log:
                t = threadingCurrentThread()
                back = frame.f_back
                if not back:
                    return
                _, name, back_base = pydevd_file_utils.get_abs_path_real_path_and_base_from_frame(
                    back)
                event_time = cur_time() - self.start_time
                method_name = frame.f_code.co_name

                if isinstance(self_obj, threading.Thread):
                    if not hasattr(self_obj, "_pydev_run_patched"):
                        wrap_attr(self_obj, "run")
                    if (method_name in THREAD_METHODS) and (back_base not in DONT_TRACE_THREADING or \
                            (method_name in INNER_METHODS and back_base in INNER_FILES)):
                        thread_id = get_thread_id(self_obj)
                        name = self_obj.getName()
                        real_method = frame.f_code.co_name
                        parent = None
                        if real_method == "_stop":
                            if back_base in INNER_FILES and \
                                            back.f_code.co_name == "_wait_for_tstate_lock":
                                back = back.f_back.f_back
                            real_method = "stop"
                            if hasattr(self_obj, "_pydev_join_called"):
                                parent = get_thread_id(t)
                        elif real_method == "join":
                            # join called in the current thread, not in self object
                            if not self_obj.is_alive():
                                return
                            thread_id = get_thread_id(t)
                            name = t.getName()
                            self_obj._pydev_join_called = True

                        if real_method == "start":
                            parent = get_thread_id(t)
                        send_message("threading_event",
                                     event_time,
                                     name,
                                     thread_id,
                                     "thread",
                                     real_method,
                                     back.f_code.co_filename,
                                     back.f_lineno,
                                     back,
                                     parent=parent)
                        # print(event_time, self_obj.getName(), thread_id, "thread",
                        #       real_method, back.f_code.co_filename, back.f_lineno)

                if method_name == "pydev_after_run_call":
                    if hasattr(frame, "f_back") and frame.f_back is not None:
                        back = frame.f_back
                        if hasattr(back, "f_back") and back.f_back is not None:
                            back = back.f_back
                        if "self" in back.f_locals:
                            if isinstance(back.f_locals["self"],
                                          threading.Thread):
                                my_self_obj = frame.f_back.f_back.f_locals[
                                    "self"]
                                my_back = frame.f_back.f_back
                                my_thread_id = get_thread_id(my_self_obj)
                                send_massage = True
                                if IS_PY3K and hasattr(my_self_obj,
                                                       "_pydev_join_called"):
                                    send_massage = False
                                    # we can't detect stop after join in Python 2 yet
                                if send_massage:
                                    send_message("threading_event",
                                                 event_time,
                                                 "Thread",
                                                 my_thread_id,
                                                 "thread",
                                                 "stop",
                                                 my_back.f_code.co_filename,
                                                 my_back.f_lineno,
                                                 my_back,
                                                 parent=None)

                if self_obj.__class__ == ObjectWrapper:
                    if back_base in DONT_TRACE_THREADING:
                        # do not trace methods called from threading
                        return
                    back_back_base = pydevd_file_utils.get_abs_path_real_path_and_base_from_frame(
                        back.f_back)[-1]
                    back = back.f_back
                    if back_back_base in DONT_TRACE_THREADING:
                        # back_back_base is the file, where the method was called froms
                        return
                    if method_name == "__init__":
                        send_message("threading_event",
                                     event_time,
                                     t.getName(),
                                     get_thread_id(t),
                                     "lock",
                                     method_name,
                                     back.f_code.co_filename,
                                     back.f_lineno,
                                     back,
                                     lock_id=str(id(frame.f_locals["self"])))
                    if "attr" in frame.f_locals and \
                            (frame.f_locals["attr"] in LOCK_METHODS or
                            frame.f_locals["attr"] in QUEUE_METHODS):
                        real_method = frame.f_locals["attr"]
                        if method_name == "call_begin":
                            real_method += "_begin"
                        elif method_name == "call_end":
                            real_method += "_end"
                        else:
                            return
                        if real_method == "release_end":
                            # do not log release end. Maybe use it later
                            return
                        send_message("threading_event",
                                     event_time,
                                     t.getName(),
                                     get_thread_id(t),
                                     "lock",
                                     real_method,
                                     back.f_code.co_filename,
                                     back.f_lineno,
                                     back,
                                     lock_id=str(id(self_obj)))

                        if real_method in ("put_end", "get_end"):
                            # fake release for queue, cause we don't call it directly
                            send_message("threading_event",
                                         event_time,
                                         t.getName(),
                                         get_thread_id(t),
                                         "lock",
                                         "release",
                                         back.f_code.co_filename,
                                         back.f_lineno,
                                         back,
                                         lock_id=str(id(self_obj)))
                        # print(event_time, t.getName(), get_thread_id(t), "lock",
                        #       real_method, back.f_code.co_filename, back.f_lineno)

        except Exception:
            traceback.print_exc()
예제 #12
0
    def make_thread_stack_str(self, frame, frame_id_to_lineno=None):
        '''
        :param frame_id_to_lineno:
            If available, the line number for the frame will be gotten from this dict,
            otherwise frame.f_lineno will be used (needed for unhandled exceptions as
            the place where we report may be different from the place where it's raised).
        '''
        if frame_id_to_lineno is None:
            frame_id_to_lineno = {}
        make_valid_xml_value = pydevd_xml.make_valid_xml_value
        cmd_text_list = []
        append = cmd_text_list.append

        curr_frame = frame
        frame = None  # Clear frame reference
        try:
            py_db = get_global_debugger()
            while curr_frame:
                frame_id = id(curr_frame)

                if curr_frame.f_code is None:
                    break  # Iron Python sometimes does not have it!

                method_name = curr_frame.f_code.co_name  # method name (if in method) or ? if global
                if method_name is None:
                    break  # Iron Python sometimes does not have it!

                abs_path_real_path_and_base = get_abs_path_real_path_and_base_from_frame(
                    curr_frame)
                if py_db.get_file_type(
                        abs_path_real_path_and_base) == py_db.PYDEV_FILE:
                    # Skip pydevd files.
                    curr_frame = curr_frame.f_back
                    continue

                filename_in_utf8 = pydevd_file_utils.norm_file_to_client(
                    abs_path_real_path_and_base[0])
                if not filesystem_encoding_is_utf8 and hasattr(
                        filename_in_utf8, "decode"):
                    # filename_in_utf8 is a byte string encoded using the file system encoding
                    # convert it to utf8
                    filename_in_utf8 = filename_in_utf8.decode(
                        file_system_encoding).encode("utf-8")

                # print("file is ", filename_in_utf8)

                lineno = frame_id_to_lineno.get(frame_id, curr_frame.f_lineno)
                # print("line is ", lineno)

                # Note: variables are all gotten 'on-demand'.
                append('<frame id="%s" name="%s" ' %
                       (frame_id, make_valid_xml_value(method_name)))
                append('file="%s" line="%s">' %
                       (quote(make_valid_xml_value(filename_in_utf8),
                              '/>_= \t'), lineno))
                append("</frame>")
                curr_frame = curr_frame.f_back
        except:
            traceback.print_exc()

        curr_frame = None  # Clear frame reference
        return ''.join(cmd_text_list)
예제 #13
0
    def __call__(self, frame, event, arg):
        ''' This is the callback used when we enter some context in the debugger.

        We also decorate the thread we are in with info about the debugging.
        The attributes added are:
            pydev_state
            pydev_step_stop
            pydev_step_cmd
            pydev_notify_kill

        :param PyDB py_db:
            This is the global debugger (this method should actually be added as a method to it).
        '''
        # IFDEF CYTHON
        # cdef str filename;
        # cdef str base;
        # cdef tuple abs_path_real_path_and_base;
        # cdef PyDBAdditionalThreadInfo additional_info;
        # ENDIF
        py_db, t, additional_info = self._args

        try:
            if py_db._finish_debugging_session:
                if not py_db._termination_event_set:
                    #that was not working very well because jython gave some socket errors
                    try:
                        if py_db.output_checker is None:
                            kill_all_pydev_threads()
                    except:
                        traceback.print_exc()
                    py_db._termination_event_set = True
                return None

            # if thread is not alive, cancel trace_dispatch processing
            if not is_thread_alive(t):
                py_db._process_thread_not_alive(get_thread_id(t))
                return None  # suspend tracing

            try:
                # Make fast path faster!
                abs_path_real_path_and_base = NORM_PATHS_AND_BASE_CONTAINER[frame.f_code.co_filename]
            except:
                abs_path_real_path_and_base = get_abs_path_real_path_and_base_from_frame(frame)

            if py_db.thread_analyser is not None:
                py_db.thread_analyser.log_event(frame)

            if py_db.asyncio_analyser is not None:
                py_db.asyncio_analyser.log_event(frame)

            file_type = get_file_type(abs_path_real_path_and_base[-1]) #we don't want to debug threading or anything related to pydevd

            if file_type is not None:
                if file_type == 1: # inlining LIB_FILE = 1
                    if py_db.not_in_scope(abs_path_real_path_and_base[1]):
                        # print('skipped: trace_dispatch (not in scope)', base, frame.f_lineno, event, frame.f_code.co_name, file_type)
                        return None
                else:
                    # print('skipped: trace_dispatch', base, frame.f_lineno, event, frame.f_code.co_name, file_type)
                    return None

            if additional_info.pydev_step_cmd != -1:
                if py_db.is_filter_enabled and py_db.is_ignored_by_filters(abs_path_real_path_and_base[1]):
                    # ignore files matching stepping filters
                    return None
                if py_db.is_filter_libraries and py_db.not_in_scope(abs_path_real_path_and_base[1]):
                    # ignore library files while stepping
                    return None

            # print('trace_dispatch', base, frame.f_lineno, event, frame.f_code.co_name, file_type)
            if additional_info.is_tracing:
                return None  #we don't wan't to trace code invoked from pydevd_frame.trace_dispatch


            # each new frame...
            # IFDEF CYTHON
            # # Note that on Cython we only support more modern idioms (no support for < Python 2.5)
            # return PyDBFrame((py_db, abs_path_real_path_and_base[1], additional_info, t)).trace_dispatch(frame, event, arg)
            # ELSE
            return additional_info.create_db_frame((py_db, abs_path_real_path_and_base[1], additional_info, t, frame)).trace_dispatch(frame, event, arg)
            # ENDIF

        except SystemExit:
            return None

        except Exception:
            if py_db._finish_debugging_session:
                return None # Don't log errors when we're shutting down.
            # Log it
            try:
                if traceback is not None:
                    # This can actually happen during the interpreter shutdown in Python 2.7
                    traceback.print_exc()
            except:
                # Error logging? We're really in the interpreter shutdown...
                # (https://github.com/fabioz/PyDev.Debugger/issues/8)
                pass
            return None
예제 #14
0
    def __call__(self, frame, event, arg):
        ''' This is the callback used when we enter some context in the debugger.

        We also decorate the thread we are in with info about the debugging.
        The attributes added are:
            pydev_state
            pydev_step_stop
            pydev_step_cmd
            pydev_notify_kill

        :param PyDB py_db:
            This is the global debugger (this method should actually be added as a method to it).
        '''
        # IFDEF CYTHON
        # cdef str filename;
        # cdef str base;
        # cdef int pydev_step_cmd;
        # cdef tuple frame_cache_key;
        # cdef dict cache_skips;
        # cdef bint is_stepping;
        # cdef tuple abs_path_real_path_and_base;
        # cdef PyDBAdditionalThreadInfo additional_info;
        # ENDIF

        # DEBUG = 'code_to_debug' in frame.f_code.co_filename
        # if DEBUG: print('ENTER: trace_dispatch: %s %s %s %s' % (frame.f_code.co_filename, frame.f_lineno, event, frame.f_code.co_name))
        py_db, t, additional_info, cache_skips, frame_skips_cache = self._args
        pydev_step_cmd = additional_info.pydev_step_cmd
        is_stepping = pydev_step_cmd != -1

        try:
            if py_db._finish_debugging_session:
                if not py_db._termination_event_set:
                    # that was not working very well because jython gave some socket errors
                    try:
                        if py_db.output_checker_thread is None:
                            kill_all_pydev_threads()
                    except:
                        pydev_log_exception()
                    py_db._termination_event_set = True
                return None if event == 'call' else NO_FTRACE

            # if thread is not alive, cancel trace_dispatch processing
            if not is_thread_alive(t):
                py_db.notify_thread_not_alive(get_current_thread_id(t))
                return None if event == 'call' else NO_FTRACE

            if py_db.thread_analyser is not None:
                py_db.thread_analyser.log_event(frame)

            if py_db.asyncio_analyser is not None:
                py_db.asyncio_analyser.log_event(frame)

            # Note: it's important that the context name is also given because we may hit something once
            # in the global context and another in the local context.
            frame_cache_key = (frame.f_code.co_firstlineno,
                               frame.f_code.co_name, frame.f_code.co_filename)
            if frame_cache_key in cache_skips:
                if not is_stepping:
                    # if DEBUG: print('skipped: trace_dispatch (cache hit)', frame_cache_key, frame.f_lineno, event, frame.f_code.co_name)
                    return None if event == 'call' else NO_FTRACE
                else:
                    # When stepping we can't take into account caching based on the breakpoints (only global filtering).
                    if cache_skips.get(frame_cache_key) == 1:

                        if additional_info.pydev_original_step_cmd in (
                                CMD_STEP_INTO, CMD_STEP_INTO_MY_CODE
                        ) and not _global_notify_skipped_step_in:
                            notify_skipped_step_in_because_of_filters(
                                py_db, frame)

                        back_frame = frame.f_back
                        if back_frame is not None and pydev_step_cmd in (
                                CMD_STEP_INTO, CMD_STEP_INTO_MY_CODE,
                                CMD_STEP_RETURN, CMD_STEP_RETURN_MY_CODE):
                            back_frame_cache_key = (
                                back_frame.f_code.co_firstlineno,
                                back_frame.f_code.co_name,
                                back_frame.f_code.co_filename)
                            if cache_skips.get(back_frame_cache_key) == 1:
                                # if DEBUG: print('skipped: trace_dispatch (cache hit: 1)', frame_cache_key, frame.f_lineno, event, frame.f_code.co_name)
                                return None if event == 'call' else NO_FTRACE
                        else:
                            # if DEBUG: print('skipped: trace_dispatch (cache hit: 2)', frame_cache_key, frame.f_lineno, event, frame.f_code.co_name)
                            return None if event == 'call' else NO_FTRACE

            try:
                # Make fast path faster!
                abs_path_real_path_and_base = NORM_PATHS_AND_BASE_CONTAINER[
                    frame.f_code.co_filename]
            except:
                abs_path_real_path_and_base = get_abs_path_real_path_and_base_from_frame(
                    frame)

            filename = abs_path_real_path_and_base[1]
            file_type = py_db.get_file_type(
                abs_path_real_path_and_base
            )  # we don't want to debug threading or anything related to pydevd

            if file_type is not None:
                if file_type == 1:  # inlining LIB_FILE = 1
                    if not py_db.in_project_scope(filename):
                        # if DEBUG: print('skipped: trace_dispatch (not in scope)', abs_path_real_path_and_base[-1], frame.f_lineno, event, frame.f_code.co_name, file_type)
                        cache_skips[frame_cache_key] = 1
                        return None if event == 'call' else NO_FTRACE
                else:
                    # if DEBUG: print('skipped: trace_dispatch', abs_path_real_path_and_base[-1], frame.f_lineno, event, frame.f_code.co_name, file_type)
                    cache_skips[frame_cache_key] = 1
                    return None if event == 'call' else NO_FTRACE

            if py_db.is_files_filter_enabled:
                if py_db.apply_files_filter(frame, filename, False):
                    cache_skips[frame_cache_key] = 1

                    if is_stepping and additional_info.pydev_original_step_cmd in (
                            CMD_STEP_INTO, CMD_STEP_INTO_MY_CODE
                    ) and not _global_notify_skipped_step_in:
                        notify_skipped_step_in_because_of_filters(py_db, frame)

                    # A little gotcha, sometimes when we're stepping in we have to stop in a
                    # return event showing the back frame as the current frame, so, we need
                    # to check not only the current frame but the back frame too.
                    back_frame = frame.f_back
                    if back_frame is not None and pydev_step_cmd in (
                            CMD_STEP_INTO, CMD_STEP_INTO_MY_CODE,
                            CMD_STEP_RETURN, CMD_STEP_RETURN_MY_CODE):
                        if py_db.apply_files_filter(
                                back_frame, back_frame.f_code.co_filename,
                                False):
                            back_frame_cache_key = (
                                back_frame.f_code.co_firstlineno,
                                back_frame.f_code.co_name,
                                back_frame.f_code.co_filename)
                            cache_skips[back_frame_cache_key] = 1
                            # if DEBUG: print('skipped: trace_dispatch (filtered out: 1)', frame_cache_key, frame.f_lineno, event, frame.f_code.co_name)

                            return None if event == 'call' else NO_FTRACE
                    else:
                        # if DEBUG: print('skipped: trace_dispatch (filtered out: 2)', frame_cache_key, frame.f_lineno, event, frame.f_code.co_name)
                        return None if event == 'call' else NO_FTRACE

            # if DEBUG: print('trace_dispatch', filename, frame.f_lineno, event, frame.f_code.co_name, file_type)
            if additional_info.is_tracing:
                return None if event == 'call' else NO_FTRACE  # we don't wan't to trace code invoked from pydevd_frame.trace_dispatch

            # Just create PyDBFrame directly (removed support for Python versions < 2.5, which required keeping a weak
            # reference to the frame).
            ret = PyDBFrame((
                py_db,
                filename,
                additional_info,
                t,
                frame_skips_cache,
                frame_cache_key,
            )).trace_dispatch(frame, event, arg)
            if ret is None:
                # 1 means skipped because of filters.
                # 2 means skipped because no breakpoints were hit.
                cache_skips[frame_cache_key] = 2
                return None if event == 'call' else NO_FTRACE

            # IFDEF CYTHON
            # frame.f_trace = SafeCallWrapper(ret)  # Make sure we keep the returned tracer.
            # ELSE
            frame.f_trace = ret  # Make sure we keep the returned tracer.
            # ENDIF
            return ret

        except SystemExit:
            return None if event == 'call' else NO_FTRACE

        except Exception:
            if py_db._finish_debugging_session:
                return None if event == 'call' else NO_FTRACE  # Don't log errors when we're shutting down.
            # Log it
            try:
                if pydev_log_exception is not None:
                    # This can actually happen during the interpreter shutdown in Python 2.7
                    pydev_log_exception()
            except:
                # Error logging? We're really in the interpreter shutdown...
                # (https://github.com/fabioz/PyDev.Debugger/issues/8)
                pass
            return None if event == 'call' else NO_FTRACE
    def __call__(self, frame, event, arg):
        ''' This is the callback used when we enter some context in the debugger.

        We also decorate the thread we are in with info about the debugging.
        The attributes added are:
            pydev_state
            pydev_step_stop
            pydev_step_cmd
            pydev_notify_kill

        :param PyDB py_db:
            This is the global debugger (this method should actually be added as a method to it).
        '''
        # IFDEF CYTHON
        # cdef str filename;
        # cdef str base;
        # cdef int pydev_step_cmd;
        # cdef tuple frame_cache_key;
        # cdef dict cache_skips;
        # cdef bint is_stepping;
        # cdef tuple abs_path_real_path_and_base;
        # cdef PyDBAdditionalThreadInfo additional_info;
        # ENDIF
        # print('ENTER: trace_dispatch', frame.f_code.co_filename, frame.f_lineno, event, frame.f_code.co_name)
        py_db, t, additional_info, cache_skips, frame_skips_cache = self._args
        pydev_step_cmd = additional_info.pydev_step_cmd
        is_stepping = pydev_step_cmd != -1

        try:
            if py_db._finish_debugging_session:
                if not py_db._termination_event_set:
                    # that was not working very well because jython gave some socket errors
                    try:
                        if py_db.output_checker_thread is None:
                            kill_all_pydev_threads()
                    except:
                        traceback.print_exc()
                    py_db._termination_event_set = True
                if event != 'call': frame.f_trace = NO_FTRACE
                return None

            # if thread is not alive, cancel trace_dispatch processing
            if not is_thread_alive(t):
                py_db.notify_thread_not_alive(get_current_thread_id(t))
                if event != 'call': frame.f_trace = NO_FTRACE
                return None  # suspend tracing

            if py_db.thread_analyser is not None:
                py_db.thread_analyser.log_event(frame)

            if py_db.asyncio_analyser is not None:
                py_db.asyncio_analyser.log_event(frame)

            # Note: it's important that the context name is also given because we may hit something once
            # in the global context and another in the local context.
            frame_cache_key = (frame.f_code.co_firstlineno, frame.f_code.co_name, frame.f_code.co_filename)
            if not is_stepping and frame_cache_key in cache_skips:
                # print('skipped: trace_dispatch (cache hit)', frame_cache_key, frame.f_lineno, event, frame.f_code.co_name)
                if event != 'call': frame.f_trace = NO_FTRACE
                return None

            try:
                # Make fast path faster!
                abs_path_real_path_and_base = NORM_PATHS_AND_BASE_CONTAINER[frame.f_code.co_filename]
            except:
                abs_path_real_path_and_base = get_abs_path_real_path_and_base_from_frame(frame)

            filename = abs_path_real_path_and_base[1]
            file_type = get_file_type(abs_path_real_path_and_base[-1])  # we don't want to debug threading or anything related to pydevd

            if file_type is not None:
                if file_type == 1:  # inlining LIB_FILE = 1
                    if not py_db.in_project_scope(filename):
                        # print('skipped: trace_dispatch (not in scope)', abs_path_real_path_and_base[-1], frame.f_lineno, event, frame.f_code.co_name, file_type)
                        cache_skips[frame_cache_key] = 1
                        if event != 'call': frame.f_trace = NO_FTRACE
                        return None
                else:
                    # print('skipped: trace_dispatch', abs_path_real_path_and_base[-1], frame.f_lineno, event, frame.f_code.co_name, file_type)
                    cache_skips[frame_cache_key] = 1
                    if event != 'call': frame.f_trace = NO_FTRACE
                    return None

            if is_stepping:
                if py_db.is_filter_enabled and py_db.is_ignored_by_filters(filename):
                    # ignore files matching stepping filters
                    if event != 'call': frame.f_trace = NO_FTRACE
                    return None
                if py_db.is_filter_libraries and not py_db.in_project_scope(filename):
                    # ignore library files while stepping
                    if event != 'call': frame.f_trace = NO_FTRACE
                    return None

            # print('trace_dispatch', base, frame.f_lineno, event, frame.f_code.co_name, file_type)
            if additional_info.is_tracing:
                if event != 'call': frame.f_trace = NO_FTRACE
                return None  # we don't wan't to trace code invoked from pydevd_frame.trace_dispatch

            # Just create PyDBFrame directly (removed support for Python versions < 2.5, which required keeping a weak
            # reference to the frame).
            ret = PyDBFrame(
                (
                    py_db, filename, additional_info, t, frame_skips_cache, frame_cache_key,
                )
            ).trace_dispatch(frame, event, arg)
            if ret is None:
                cache_skips[frame_cache_key] = 1
                if event != 'call': frame.f_trace = NO_FTRACE
                return None

            # IFDEF CYTHON
            # ret = SafeCallWrapper(ret)
            # ENDIF
            frame.f_trace = ret  # Make sure we keep the returned tracer.
            return ret

        except SystemExit:
            if event != 'call': frame.f_trace = NO_FTRACE
            return None

        except Exception:
            if py_db._finish_debugging_session:
                if event != 'call': frame.f_trace = NO_FTRACE
                return None  # Don't log errors when we're shutting down.
            # Log it
            try:
                if traceback is not None:
                    # This can actually happen during the interpreter shutdown in Python 2.7
                    traceback.print_exc()
            except:
                # Error logging? We're really in the interpreter shutdown...
                # (https://github.com/fabioz/PyDev.Debugger/issues/8)
                pass
            if event != 'call': frame.f_trace = NO_FTRACE
            return None
    def log_event(self, frame):
        write_log = False
        self_obj = None
        if dict_contains(frame.f_locals, "self"):
            self_obj = frame.f_locals["self"]
            if isinstance(self_obj, threading.Thread) or self_obj.__class__ == ObjectWrapper:
                write_log = True
        if hasattr(frame, "f_back") and frame.f_back is not None:
            back = frame.f_back
            if hasattr(back, "f_back") and back.f_back is not None:
                back = back.f_back
                if dict_contains(back.f_locals, "self"):
                    if isinstance(back.f_locals["self"], threading.Thread):
                        write_log = True
        try:
            if write_log:
                t = threadingCurrentThread()
                back = frame.f_back
                if not back:
                    return
                _, name, back_base = pydevd_file_utils.get_abs_path_real_path_and_base_from_frame(back)
                event_time = cur_time() - self.start_time
                method_name = frame.f_code.co_name

                if isinstance(self_obj, threading.Thread):
                    if not hasattr(self_obj, "_pydev_run_patched"):
                        wrap_attr(self_obj, "run")
                    if (method_name in THREAD_METHODS) and (back_base not in DONT_TRACE_THREADING or \
                            (method_name in INNER_METHODS and back_base in INNER_FILES)):
                        thread_id = get_thread_id(self_obj)
                        name = self_obj.getName()
                        real_method = frame.f_code.co_name
                        parent = None
                        if real_method == "_stop":
                            if back_base in INNER_FILES and \
                                            back.f_code.co_name == "_wait_for_tstate_lock":
                                back = back.f_back.f_back
                            real_method = "stop"
                            if hasattr(self_obj, "_pydev_join_called"):
                                parent = get_thread_id(t)
                        elif real_method == "join":
                            # join called in the current thread, not in self object
                            if not self_obj.is_alive():
                                return
                            thread_id = get_thread_id(t)
                            name = t.getName()
                            setattr(self_obj, "_pydev_join_called", True)

                        if real_method == "start":
                            parent = get_thread_id(t)
                        send_message("threading_event", event_time, name, thread_id, "thread",
                        real_method, back.f_code.co_filename, back.f_lineno, back, parent=parent)
                        # print(event_time, self_obj.getName(), thread_id, "thread",
                        #       real_method, back.f_code.co_filename, back.f_lineno)

                if method_name == "pydev_after_run_call":
                    if hasattr(frame, "f_back") and frame.f_back is not None:
                        back = frame.f_back
                        if hasattr(back, "f_back") and back.f_back is not None:
                            back = back.f_back
                        if dict_contains(back.f_locals, "self"):
                            if isinstance(back.f_locals["self"], threading.Thread):
                                my_self_obj = frame.f_back.f_back.f_locals["self"]
                                my_back = frame.f_back.f_back
                                my_thread_id = get_thread_id(my_self_obj)
                                send_massage = True
                                if IS_PY3K and hasattr(my_self_obj, "_pydev_join_called"):
                                    send_massage = False
                                    # we can't detect stop after join in Python 2 yet
                                if send_massage:
                                    send_message("threading_event", event_time, "Thread", my_thread_id, "thread",
                                                 "stop", my_back.f_code.co_filename, my_back.f_lineno, my_back, parent=None)

                if self_obj.__class__ == ObjectWrapper:
                    if back_base in DONT_TRACE_THREADING:
                        # do not trace methods called from threading
                        return
                    back_back_base = pydevd_file_utils.get_abs_path_real_path_and_base_from_frame(back.f_back)[-1]
                    back = back.f_back
                    if back_back_base in DONT_TRACE_THREADING:
                        # back_back_base is the file, where the method was called froms
                        return
                    if method_name == "__init__":
                        send_message("threading_event", event_time, t.getName(), get_thread_id(t), "lock",
                                     method_name, back.f_code.co_filename, back.f_lineno, back, lock_id=str(id(frame.f_locals["self"])))
                    if dict_contains(frame.f_locals, "attr") and \
                            (frame.f_locals["attr"] in LOCK_METHODS or
                            frame.f_locals["attr"] in QUEUE_METHODS):
                        real_method = frame.f_locals["attr"]
                        if method_name == "call_begin":
                            real_method += "_begin"
                        elif method_name == "call_end":
                            real_method += "_end"
                        else:
                            return
                        if real_method == "release_end":
                            # do not log release end. Maybe use it later
                            return
                        send_message("threading_event", event_time, t.getName(), get_thread_id(t), "lock",
                        real_method, back.f_code.co_filename, back.f_lineno, back, lock_id=str(id(self_obj)))

                        if real_method in ("put_end", "get_end"):
                            # fake release for queue, cause we don't call it directly
                            send_message("threading_event", event_time, t.getName(), get_thread_id(t), "lock",
                                         "release", back.f_code.co_filename, back.f_lineno, back, lock_id=str(id(self_obj)))
                        # print(event_time, t.getName(), get_thread_id(t), "lock",
                        #       real_method, back.f_code.co_filename, back.f_lineno)


        except Exception:
            traceback.print_exc()
예제 #17
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
예제 #18
0
    def handle_exception(self, frame, event, arg):
        try:
            # print 'handle_exception', frame.f_lineno, frame.f_code.co_name

            # We have 3 things in arg: exception type, description, traceback object
            trace_obj = arg[2]
            main_debugger = self._args[0]

            if not hasattr(trace_obj, 'tb_next'):
                return  #Not always there on Jython...

            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).

                if main_debugger.break_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
            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:
                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]


                    filename_to_lines_where_exceptions_are_ignored = self.filename_to_lines_where_exceptions_are_ignored


                    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 = self.filename_to_stat_info.get(filename)
                    if last_stat != curr_stat:
                        self.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 = self._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_thread_id(thread)
                pydevd_vars.add_additional_frame_by_id(thread_id, frame_id_to_frame)
                try:
                    main_debugger.send_caught_exception_stack(thread, arg, id(frame))
                    self.set_suspend(thread, CMD_STEP_CAUGHT_EXCEPTION)
                    self.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:
                traceback.print_exc()

            main_debugger.set_trace_for_frame_and_parents(frame)
        finally:
            #Clear some local variables...
            trace_obj = None
            initial_trace_obj = None
            check_trace_obj = None
            f = None
            frame_id_to_frame = None
            main_debugger = None
            thread = None
예제 #19
0
    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', frame.f_lineno, frame.f_code.co_name, 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

            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:
                    flag, frame = self.should_stop_on_exception(
                        frame, event, arg)
                    if flag:
                        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:
                    # I believe this can only happen in jython on some frontiers on jython and java code, which we don't want to trace.
                    return None

            need_trace_return = False
            if is_call and main_debugger.signature_factory:
                need_trace_return = send_signature_call_trace(
                    main_debugger, frame, filename)
            if is_return and main_debugger.signature_factory:
                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, info)

                        # CMD_STEP_OVER = 108
                        if can_skip and is_return 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_trace_return:
                                return self.trace_return
                            else:
                                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:
                            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>'):
                            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_trace_return:
                                return self.trace_return
                            else:
                                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', frame.f_lineno, frame.f_code.co_name, event)

            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:
                        condition = breakpoint.condition
                        if condition is not None:
                            eval_result = handle_breakpoint_condition(
                                main_debugger, info, breakpoint, new_frame)
                            if not eval_result:
                                return self.trace_dispatch

                        if breakpoint.expression is not None:
                            handle_breakpoint_expression(
                                breakpoint, info, new_frame)

                        if not main_debugger.first_breakpoint_reached:
                            if is_call:
                                back = frame.f_back
                                if back is not None:
                                    # When we start debug session, we call execfile in pydevd run function. It produces an additional
                                    # 'call' event for tracing and we stop on the first line of code twice.
                                    _, back_filename, base = get_abs_path_real_path_and_base_from_frame(
                                        back)
                                    if (base == DEBUG_START[0] and back.f_code.co_name == DEBUG_START[1]) or \
                                            (base == DEBUG_START_PY3K[0] and back.f_code.co_name == DEBUG_START_PY3K[1]):
                                        stop = False
                                        main_debugger.first_breakpoint_reached = True
                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 main_debugger.not_in_scope(
                                filename):
                            # ignore library files while stepping
                            return self.trace_dispatch

                if main_debugger.show_return_values or main_debugger.remove_return_values_flag:
                    self.manage_return_values(main_debugger, frame, event, arg)

                if stop:
                    self.set_suspend(thread, CMD_SET_BREAK)
                    if breakpoint and breakpoint.suspend_policy == "ALL":
                        main_debugger.suspend_all_other_threads(thread)
                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 breakpoint is None and not (is_return
                                                   or is_exception_event):
                        # 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_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 not main_debugger.not_in_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

                elif step_cmd == CMD_RUN_TO_LINE or step_cmd == CMD_SET_NEXT_STATEMENT:
                    try:
                        stop, _, response_msg = main_debugger.set_next_statement(
                            frame, event, info.pydev_func_name,
                            info.pydev_next_line)
                    except ValueError:
                        pass
                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 == DEBUG_START[
                                    0] and back.f_code.co_name == DEBUG_START[
                                        1]:
                                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

                            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, overwrite_prev_trace=True)
                                    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:
                    return None

            #if we are quitting, let's stop the tracing
            retVal = None
            if not main_debugger.quitting:
                retVal = self.trace_dispatch

            return retVal
        finally:
            info.is_tracing = False
    def _schedule_callback(prev, next):
        '''
        Called when a context is stopped or a new context is made runnable.
        '''
        try:
            if not prev and not next:
                return

            if next:
                register_tasklet_info(next)

                # Ok, making next runnable: set the tracing facility in it.
                debugger = get_global_debugger()
                if debugger is not None and next.frame:
                    if hasattr(next.frame, 'f_trace'):
                        next.frame.f_trace = debugger.get_thread_local_trace_func(
                        )
                debugger = None

            if prev:
                register_tasklet_info(prev)

            try:
                for tasklet_ref, tasklet_info in dict_items(
                        _weak_tasklet_registered_to_info
                ):  # Make sure it's a copy!
                    tasklet = tasklet_ref()
                    if tasklet is None or not tasklet.alive:
                        # Garbage-collected already!
                        try:
                            del _weak_tasklet_registered_to_info[tasklet_ref]
                        except KeyError:
                            pass
                        if tasklet_info.frame_id is not None:
                            remove_custom_frame(tasklet_info.frame_id)
                    else:
                        if tasklet.paused or tasklet.blocked or tasklet.scheduled:
                            if tasklet.frame and tasklet.frame.f_back:
                                f_back = tasklet.frame.f_back
                                base = get_abs_path_real_path_and_base_from_frame(
                                    f_back)[-1]
                                is_file_to_ignore = base in DONT_TRACE
                                if not is_file_to_ignore:
                                    if tasklet_info.frame_id is None:
                                        tasklet_info.frame_id = add_custom_frame(
                                            f_back, tasklet_info.tasklet_name,
                                            tasklet.thread_id)
                                    else:
                                        update_custom_frame(
                                            tasklet_info.frame_id, f_back,
                                            tasklet.thread_id)

                        elif tasklet.is_current:
                            if tasklet_info.frame_id is not None:
                                # Remove info about stackless suspended when it starts to run.
                                remove_custom_frame(tasklet_info.frame_id)
                                tasklet_info.frame_id = None

            finally:
                tasklet = None
                tasklet_info = None
                f_back = None

        except:
            import traceback
            traceback.print_exc()

        if _application_set_schedule_callback is not None:
            return _application_set_schedule_callback(prev, next)
예제 #21
0
    def trace_dispatch(self, frame, event, arg):
        # ENDIF

        main_debugger, filename, info, thread = self._args
        try:
            # print 'frame trace_dispatch', frame.f_lineno, frame.f_code.co_name, event
            info.is_tracing = True

            if main_debugger._finish_debugging_session:
                return None

            if event == "call" and main_debugger.signature_factory:
                send_signature_call_trace(main_debugger, frame, filename)

            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:
                    flag, frame = self.should_stop_on_exception(frame, event, arg)
                    if flag:
                        self.handle_exception(frame, event, arg)
                        return self.trace_dispatch

            elif event not in ("line", "call", "return"):
                # I believe this can only happen in jython on some frontiers on jython and java code, which we don't want to trace.
                return None

            stop_frame = info.pydev_step_stop
            step_cmd = info.pydev_step_cmd

            if is_exception_event:
                breakpoints_for_file = None
                if (
                    stop_frame
                    and stop_frame is not frame
                    and step_cmd == CMD_STEP_OVER
                    and arg[0] in (StopIteration, GeneratorExit)
                    and arg[2] is None
                ):
                    info.pydev_step_cmd = CMD_STEP_INTO
                    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 event == "return" and step_cmd in (CMD_STEP_RETURN, CMD_STEP_OVER):
                    if not frame.f_code.co_flags & CO_GENERATOR:
                        info.pydev_step_cmd = CMD_STEP_INTO
                        info.pydev_step_stop = None

                breakpoints_for_file = main_debugger.breakpoints.get(filename)

                can_skip = False

                if info.pydev_state == STATE_RUN:
                    # 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
                    can_skip = (step_cmd == -1 and stop_frame is None) or (
                        step_cmd in (CMD_STEP_RETURN, CMD_STEP_OVER) and stop_frame is not frame
                    )

                if can_skip and plugin_manager is not None and main_debugger.has_plugin_line_breaks:
                    can_skip = not plugin_manager.can_not_skip(main_debugger, self, frame)

                if can_skip and main_debugger.show_return_values:
                    # trace function for showing return values after step over
                    if (
                        info.pydev_step_cmd == CMD_STEP_OVER
                        and hasattr(frame, "f_back")
                        and frame.f_back == info.pydev_step_stop
                    ):
                        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:
                            return None

                else:
                    # 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>"):
                        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):
                            break

                    else:  # if we had some break, it won't get here (so, that's a context that we want to skip)
                        if can_skip:
                            if has_exception_breakpoints:
                                return self.trace_exception
                            else:
                                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', frame.f_lineno, frame.f_code.co_name, event

            try:
                line = frame.f_lineno
                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 flag
                    and event != "return"
                    and info.pydev_state != STATE_SUSPEND
                    and breakpoints_for_file is not None
                    and dict_contains(breakpoints_for_file, line)
                ):
                    breakpoint = breakpoints_for_file[line]
                    new_frame = frame
                    stop = True
                    if step_cmd == CMD_STEP_OVER and stop_frame is frame and event in ("line", "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:
                        condition = breakpoint.condition
                        if condition is not None:
                            try:
                                val = eval(condition, new_frame.f_globals, new_frame.f_locals)
                                if not val:
                                    return self.trace_dispatch

                            except:
                                if type(condition) != type(""):
                                    if hasattr(condition, "encode"):
                                        condition = condition.encode("utf-8")

                                msg = "Error while evaluating expression: %s\n" % (condition,)
                                sys.stderr.write(msg)
                                traceback.print_exc()
                                if not main_debugger.suspend_on_breakpoint_exception:
                                    return self.trace_dispatch
                                else:
                                    stop = True
                                    try:
                                        # add exception_type and stacktrace into thread additional info
                                        etype, value, tb = sys.exc_info()
                                        try:
                                            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,
                                            )
                                        finally:
                                            etype, value, tb = None, None, None
                                    except:
                                        traceback.print_exc()

                        if breakpoint.expression is not None:
                            try:
                                try:
                                    val = eval(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)

                        if not main_debugger.first_breakpoint_reached:
                            if event == "call":
                                if hasattr(frame, "f_back"):
                                    back = frame.f_back
                                    if back is not None:
                                        # When we start debug session, we call execfile in pydevd run function. It produces an additional
                                        # 'call' event for tracing and we stop on the first line of code twice.
                                        _, back_filename, base = get_abs_path_real_path_and_base_from_frame(back)
                                        if (base == DEBUG_START[0] and back.f_code.co_name == DEBUG_START[1]) or (
                                            base == DEBUG_START_PY3K[0] and back.f_code.co_name == DEBUG_START_PY3K[1]
                                        ):
                                            stop = False
                                            main_debugger.first_breakpoint_reached = True
                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 main_debugger.not_in_scope(filename):
                            # ignore library files while stepping
                            return self.trace_dispatch

                if main_debugger.show_return_values or main_debugger.remove_return_values_flag:
                    self.manage_return_values(main_debugger, frame, event, arg)

                if stop:
                    self.set_suspend(thread, CMD_SET_BREAK)
                    if breakpoint and breakpoint.suspend_policy == "ALL":
                        main_debugger.suspend_all_other_threads(thread)
                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

            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 = event in ("line", "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 not main_debugger.not_in_scope(frame.f_code.co_filename):
                        stop = event == "line"

                elif step_cmd == CMD_STEP_OVER:
                    stop = stop_frame is frame and event in ("line", "return")

                    if frame.f_code.co_flags & CO_GENERATOR:
                        if event == "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 event == "line" or event == "exception":
                        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 = event == "return" and stop_frame is frame

                elif step_cmd == CMD_RUN_TO_LINE or step_cmd == CMD_SET_NEXT_STATEMENT:
                    stop = False

                    if event == "line" or event == "exception":
                        # Yes, we can only act on line events (weird hum?)
                        # Note: This code is duplicated at pydevd.py
                        # Acting on exception events after debugger breaks with exception
                        curr_func_name = frame.f_code.co_name

                        # global context is set with an empty name
                        if curr_func_name in ("?", "<module>"):
                            curr_func_name = ""

                        if curr_func_name == info.pydev_func_name:
                            line = info.pydev_next_line
                            if frame.f_lineno == line:
                                stop = True
                            else:
                                if frame.f_trace is None:
                                    frame.f_trace = self.trace_dispatch
                                frame.f_lineno = line
                                frame.f_trace = None
                                stop = True

                else:
                    stop = False

                if stop and step_cmd != -1 and IS_PY3K:
                    # in Py3k we start script via our custom `execfile` function, and we shouldn't stop there
                    # while stepping when execution is finished
                    if event == "return" and hasattr(frame, "f_back") and hasattr(frame.f_back, "f_code"):
                        back_filename = os.path.basename(frame.f_back.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 event == "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 == DEBUG_START[0] and back.f_code.co_name == DEBUG_START[1]:
                                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

                            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, overwrite_prev_trace=True)
                                    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
                            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 we are quitting, let's stop the tracing
            retVal = None
            if not main_debugger.quitting:
                retVal = self.trace_dispatch

            return retVal
        finally:
            info.is_tracing = False
예제 #22
0
    def trace_dispatch(self, frame, event, arg):
        # ENDIF
        # Note: this is a big function because most of the logic related to hitting a breakpoint and
        # stepping is contained in it. Ideally this could be split among multiple functions, but the
        # problem in this case is that in pure-python function calls are expensive and even more so
        # when tracing is on (because each function call will get an additional tracing call). We
        # try to address this by using the info.is_tracing for the fastest possible return, but the
        # cost is still high (maybe we could use code-generation in the future and make the code
        # generation be better split among what each part does).

        # DEBUG = '_debugger_case_generator.py' 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 %s, stop: %s' % (frame.f_lineno, frame.f_code.co_name, frame.f_code.co_filename, event, constant_to_str(info.pydev_step_cmd), arg, info.pydev_step_stop))
        try:
            info.is_tracing += 1
            line = frame.f_lineno
            line_cache_key = (frame_cache_key, line)

            if main_debugger.pydb_disposed:
                return None if event == 'call' else NO_FTRACE

            plugin_manager = main_debugger.plugin
            has_exception_breakpoints = main_debugger.break_on_caught_exceptions or main_debugger.has_plugin_exception_breaks

            stop_frame = info.pydev_step_stop
            step_cmd = info.pydev_step_cmd

            if frame.f_code.co_flags & 0xa0:  # 0xa0 ==  CO_GENERATOR = 0x20 | CO_COROUTINE = 0x80
                # 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.
                if event == 'line':
                    is_line = True
                    is_call = False
                    is_return = False
                    is_exception_event = False

                elif event == 'return':
                    is_line = False
                    is_call = False
                    is_return = True
                    is_exception_event = False

                    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
                    else:
                        # Tricky handling: usually when we're on a frame which is about to exit
                        # we set the step mode to step into, but in this case we'd end up in the
                        # asyncio internal machinery, which is not what we want, so, we just
                        # ask the stop frame to be a level up.
                        #
                        # Note that there's an issue here which we may want to fix in the future: if
                        # the back frame is a frame which is filtered, we won't stop properly.
                        # Solving this may not be trivial as we'd need to put a scope in the step
                        # in, but we may have to do it anyways to have a step in which doesn't end
                        # up in asyncio).
                        if stop_frame is frame:
                            if step_cmd in (CMD_STEP_OVER,
                                            CMD_STEP_OVER_MY_CODE,
                                            CMD_STEP_INTO,
                                            CMD_STEP_INTO_MY_CODE):
                                f = self._get_unfiltered_back_frame(
                                    main_debugger, frame)
                                if f is not None:
                                    info.pydev_step_cmd = CMD_STEP_INTO_COROUTINE
                                    info.pydev_step_stop = f
                                else:
                                    if step_cmd == CMD_STEP_OVER:
                                        info.pydev_step_cmd = CMD_STEP_INTO
                                        info.pydev_step_stop = None

                                    elif step_cmd == CMD_STEP_OVER_MY_CODE:
                                        info.pydev_step_cmd = CMD_STEP_INTO_MY_CODE
                                        info.pydev_step_stop = None

                            elif step_cmd == CMD_STEP_INTO_COROUTINE:
                                # We're exiting this one, so, mark the new coroutine context.
                                f = self._get_unfiltered_back_frame(
                                    main_debugger, frame)
                                if f is not None:
                                    info.pydev_step_stop = f
                                else:
                                    info.pydev_step_cmd = CMD_STEP_INTO
                                    info.pydev_step_stop = None

                elif event == 'exception':
                    breakpoints_for_file = None
                    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

                    return self.trace_dispatch
                else:
                    # event == 'call' or event == 'c_XXX'
                    return self.trace_dispatch

            else:
                if event == 'line':
                    is_line = True
                    is_call = False
                    is_return = False
                    is_exception_event = False

                elif event == 'return':
                    is_line = False
                    is_return = True
                    is_call = False
                    is_exception_event = False

                    # 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 (
                            CMD_STEP_OVER, CMD_STEP_RETURN,
                            CMD_STEP_OVER_MY_CODE, CMD_STEP_RETURN_MY_CODE):
                        if step_cmd in (CMD_STEP_OVER, CMD_STEP_RETURN):
                            info.pydev_step_cmd = CMD_STEP_INTO
                        else:
                            info.pydev_step_cmd = CMD_STEP_INTO_MY_CODE
                        info.pydev_step_stop = None

                elif event == 'call':
                    is_line = False
                    is_call = True
                    is_return = False
                    is_exception_event = False

                elif event == 'exception':
                    is_exception_event = True
                    breakpoints_for_file = None
                    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:
                    # Unexpected: just keep the same trace func (i.e.: event == 'c_XXX').
                    return self.trace_dispatch

            if not is_exception_event:
                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
                    # - we're stepping into a coroutine context and we're not in that context
                    if step_cmd == -1:
                        can_skip = True

                    elif step_cmd in (CMD_STEP_OVER, CMD_STEP_RETURN,
                                      CMD_STEP_OVER_MY_CODE,
                                      CMD_STEP_RETURN_MY_CODE
                                      ) and stop_frame is not frame:
                        can_skip = True

                    elif step_cmd == CMD_STEP_INTO_COROUTINE:
                        f = frame
                        while f is not None:
                            if f is stop_frame:
                                break
                            f = f.f_back
                        else:
                            can_skip = True

                    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)

                        if can_skip and main_debugger.show_return_values and info.pydev_step_cmd in (
                                CMD_STEP_OVER, CMD_STEP_OVER_MY_CODE
                        ) and frame.f_back is stop_frame:
                            # 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:
                            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

                        func_lines = _get_func_lines(frame.f_code)
                        if func_lines is None:
                            # This is a fallback for implementations where we can't get the function
                            # lines -- i.e.: jython (in this case clients need to provide the function
                            # name to decide on the skip or we won't be able to skip the function
                            # completely).

                            # 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 bp in dict_iter_values(
                                    breakpoints_for_file
                            ):  # jython does not support itervalues()
                                # will match either global or some function
                                if bp.func_name in ('None', curr_func_name):
                                    has_breakpoint_in_frame = True
                                    break
                        else:
                            for bp_line in breakpoints_for_file:  # iterate on keys
                                if bp_line in func_lines:
                                    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:
                            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:
                                stop = False
                        elif breakpoint.is_logpoint:
                            stop = False

                    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 is stop_frame)) or
                        (info.pydev_step_cmd in
                         (CMD_STEP_RETURN, CMD_STEP_RETURN_MY_CODE) and
                         (frame is stop_frame)) or
                        (info.pydev_step_cmd
                         in (CMD_STEP_INTO, CMD_STEP_INTO_MY_CODE,
                             CMD_STEP_INTO_COROUTINE))):
                        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:
                pydev_log.exception()
                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,
                                  CMD_STEP_INTO_COROUTINE):
                    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(
                                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
                    else:
                        stop = False

                    if stop:
                        if step_cmd == CMD_STEP_INTO_COROUTINE:
                            # i.e.: Check if we're stepping into the proper context.
                            f = frame
                            while f is not None:
                                if f is stop_frame:
                                    break
                                f = f.f_back
                            else:
                                stop = False

                    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 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(
                                frame.f_back) == 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,
                            original_step_cmd=info.pydev_original_step_cmd)
                        self.do_wait_suspend(thread, frame, event, arg)
                    elif is_return:  # 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,
                                original_step_cmd=info.pydev_original_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_original_step_cmd = -1
                            info.pydev_step_cmd = -1
                            info.pydev_state = STATE_RUN

            except KeyboardInterrupt:
                raise
            except:
                try:
                    pydev_log.exception()
                    info.pydev_original_step_cmd = -1
                    info.pydev_step_cmd = -1
                    info.pydev_step_stop = None
                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 -= 1
예제 #23
0
def _schedule_callback(prev, next):
    '''
    Called when a context is stopped or a new context is made runnable.
    '''
    try:
        if not prev and not next:
            return

        current_frame = sys._getframe()

        if next:
            register_tasklet_info(next)

            # Ok, making next runnable: set the tracing facility in it.
            debugger = get_global_debugger()
            if debugger is not None:
                next.trace_function = debugger.trace_dispatch
                frame = next.frame
                if frame is current_frame:
                    frame = frame.f_back
                if hasattr(frame, 'f_trace'):  # Note: can be None (but hasattr should cover for that too).
                    frame.f_trace = debugger.trace_dispatch

            debugger = None

        if prev:
            register_tasklet_info(prev)

        try:
            for tasklet_ref, tasklet_info in dict_items(_weak_tasklet_registered_to_info):  # Make sure it's a copy!
                tasklet = tasklet_ref()
                if tasklet is None or not tasklet.alive:
                    # Garbage-collected already!
                    try:
                        del _weak_tasklet_registered_to_info[tasklet_ref]
                    except KeyError:
                        pass
                    if tasklet_info.frame_id is not None:
                        remove_custom_frame(tasklet_info.frame_id)
                else:
                    is_running = stackless.get_thread_info(tasklet.thread_id)[1] is tasklet
                    if tasklet is prev or (tasklet is not next and not is_running):
                        # the tasklet won't run after this scheduler action:
                        # - the tasklet is the previous tasklet
                        # - it is not the next tasklet and it is not an already running tasklet
                        frame = tasklet.frame
                        if frame is current_frame:
                            frame = frame.f_back
                        if frame is not None:
                            base = get_abs_path_real_path_and_base_from_frame(frame)[-1]
                            # print >>sys.stderr, "SchedCB: %r, %d, '%s', '%s'" % (tasklet, frame.f_lineno, _filename, base)
                            is_file_to_ignore = base in DONT_TRACE
                            if not is_file_to_ignore:
                                tasklet_info.update_name()
                                if tasklet_info.frame_id is None:
                                    tasklet_info.frame_id = add_custom_frame(frame, tasklet_info.tasklet_name, tasklet.thread_id)
                                else:
                                    update_custom_frame(tasklet_info.frame_id, frame, tasklet.thread_id, name=tasklet_info.tasklet_name)

                    elif tasklet is next or is_running:
                        if tasklet_info.frame_id is not None:
                            # Remove info about stackless suspended when it starts to run.
                            remove_custom_frame(tasklet_info.frame_id)
                            tasklet_info.frame_id = None


        finally:
            tasklet = None
            tasklet_info = None
            frame = None

    except:
        import traceback;traceback.print_exc()

    if _application_set_schedule_callback is not None:
        return _application_set_schedule_callback(prev, next)
    def __call__(self, frame, event, arg):
        ''' This is the callback used when we enter some context in the debugger.

        We also decorate the thread we are in with info about the debugging.
        The attributes added are:
            pydev_state
            pydev_step_stop
            pydev_step_cmd
            pydev_notify_kill

        :param PyDB py_db:
            This is the global debugger (this method should actually be added as a method to it).
        '''
        # IFDEF CYTHON
        # cdef str filename;
        # cdef str base;
        # cdef tuple abs_path_real_path_and_base;
        # cdef PyDBAdditionalThreadInfo additional_info;
        # ENDIF
        py_db, t, additional_info = self._args

        try:
            if py_db._finish_debugging_session:
                if not py_db._termination_event_set:
                    #that was not working very well because jython gave some socket errors
                    try:
                        if py_db.output_checker is None:
                            kill_all_pydev_threads()
                    except:
                        traceback.print_exc()
                    py_db._termination_event_set = True
                return None

            # if thread is not alive, cancel trace_dispatch processing
            if not is_thread_alive(t):
                py_db._process_thread_not_alive(get_thread_id(t))
                return None  # suspend tracing

            try:
                # Make fast path faster!
                abs_path_real_path_and_base = NORM_PATHS_AND_BASE_CONTAINER[frame.f_code.co_filename]
            except:
                abs_path_real_path_and_base = get_abs_path_real_path_and_base_from_frame(frame)

            if py_db.thread_analyser is not None:
                py_db.thread_analyser.log_event(frame)

            if py_db.asyncio_analyser is not None:
                py_db.asyncio_analyser.log_event(frame)

            file_type = get_file_type(abs_path_real_path_and_base[-1]) #we don't want to debug threading or anything related to pydevd

            if file_type is not None:
                if file_type == 1: # inlining LIB_FILE = 1
                    if py_db.not_in_scope(abs_path_real_path_and_base[1]):
                        # print('skipped: trace_dispatch (not in scope)', base, frame.f_lineno, event, frame.f_code.co_name, file_type)
                        return None
                else:
                    # print('skipped: trace_dispatch', base, frame.f_lineno, event, frame.f_code.co_name, file_type)
                    return None

            if additional_info.pydev_step_cmd != -1:
                if py_db.is_filter_enabled and py_db.is_ignored_by_filters(abs_path_real_path_and_base[1]):
                    # ignore files matching stepping filters
                    return None
                if py_db.is_filter_libraries and py_db.not_in_scope(abs_path_real_path_and_base[1]):
                    # ignore library files while stepping
                    return None

            # print('trace_dispatch', base, frame.f_lineno, event, frame.f_code.co_name, file_type)
            if additional_info.is_tracing:
                return None  #we don't wan't to trace code invoked from pydevd_frame.trace_dispatch


            # each new frame...
            # IFDEF CYTHON
            # # Note that on Cython we only support more modern idioms (no support for < Python 2.5)
            # return PyDBFrame((py_db, abs_path_real_path_and_base[1], additional_info, t)).trace_dispatch(frame, event, arg)
            # ELSE
            return additional_info.create_db_frame((py_db, abs_path_real_path_and_base[1], additional_info, t, frame)).trace_dispatch(frame, event, arg)
            # ENDIF

        except SystemExit:
            return None

        except Exception:
            if py_db._finish_debugging_session:
                return None # Don't log errors when we're shutting down.
            # Log it
            try:
                if traceback is not None:
                    # This can actually happen during the interpreter shutdown in Python 2.7
                    traceback.print_exc()
            except:
                # Error logging? We're really in the interpreter shutdown...
                # (https://github.com/fabioz/PyDev.Debugger/issues/8)
                pass
            return None
예제 #25
0
    def trace_dispatch(self, frame, event, arg):
        # ENDIF

        main_debugger, filename, info, thread = self._args
        try:
            # print 'frame trace_dispatch', frame.f_lineno, frame.f_code.co_name, event
            info.is_tracing = True
            need_trace_return = False

            if main_debugger._finish_debugging_session:
                return None

            if event == 'call' and main_debugger.signature_factory:
                need_trace_return = send_signature_call_trace(
                    main_debugger, frame, filename)
            if event == 'return' and main_debugger.signature_factory:
                send_signature_return_trace(main_debugger, frame, filename,
                                            arg)

            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:
                    flag, frame = self.should_stop_on_exception(
                        frame, event, arg)
                    if flag:
                        self.handle_exception(frame, event, arg)
                        return self.trace_dispatch

            elif event not in ('line', 'call', 'return'):
                #I believe this can only happen in jython on some frontiers on jython and java code, which we don't want to trace.
                return None

            stop_frame = info.pydev_step_stop
            step_cmd = info.pydev_step_cmd

            if is_exception_event:
                breakpoints_for_file = None
                if stop_frame and stop_frame is not frame and step_cmd is CMD_STEP_OVER and \
                                arg[0] in (StopIteration, GeneratorExit) and arg[2] is None:
                    info.pydev_step_cmd = CMD_STEP_INTO
                    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 event is 'return' and step_cmd in (
                        CMD_STEP_RETURN, CMD_STEP_OVER):
                    if not frame.f_code.co_flags & CO_GENERATOR:
                        info.pydev_step_cmd = CMD_STEP_INTO
                        info.pydev_step_stop = None

                breakpoints_for_file = main_debugger.breakpoints.get(filename)

                can_skip = False

                if info.pydev_state == STATE_RUN:
                    #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
                    can_skip = (step_cmd == -1 and stop_frame is None)\
                        or (step_cmd in (CMD_STEP_RETURN, CMD_STEP_OVER) and stop_frame is not frame)

                if can_skip and plugin_manager is not None and main_debugger.has_plugin_line_breaks:
                    can_skip = not plugin_manager.can_not_skip(
                        main_debugger, self, frame)

                if can_skip and main_debugger.show_return_values:
                    # trace function for showing return values after step over
                    if info.pydev_step_cmd == CMD_STEP_OVER and hasattr(
                            frame,
                            "f_back") and frame.f_back == info.pydev_step_stop:
                        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_trace_return:
                                return self.trace_return
                            else:
                                return None

                else:
                    #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>'):
                        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):
                            break

                    else:  # if we had some break, it won't get here (so, that's a context that we want to skip)
                        if can_skip:
                            if has_exception_breakpoints:
                                return self.trace_exception
                            else:
                                if need_trace_return:
                                    return self.trace_return
                                else:
                                    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', frame.f_lineno, frame.f_code.co_name, event

            try:
                line = frame.f_lineno
                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 flag and event != 'return' and info.pydev_state != STATE_SUSPEND and breakpoints_for_file is not None \
                        and dict_contains(breakpoints_for_file, line):
                    breakpoint = breakpoints_for_file[line]
                    new_frame = frame
                    stop = True
                    if step_cmd == CMD_STEP_OVER and stop_frame is frame and event in (
                            'line', '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:
                        condition = breakpoint.condition
                        if condition is not None:
                            try:
                                val = eval(condition, new_frame.f_globals,
                                           new_frame.f_locals)
                                if not val:
                                    return self.trace_dispatch

                            except:
                                if type(condition) != type(''):
                                    if hasattr(condition, 'encode'):
                                        condition = condition.encode('utf-8')

                                msg = 'Error while evaluating expression: %s\n' % (
                                    condition, )
                                sys.stderr.write(msg)
                                traceback.print_exc()
                                if not main_debugger.suspend_on_breakpoint_exception:
                                    return self.trace_dispatch
                                else:
                                    stop = True
                                    try:
                                        # add exception_type and stacktrace into thread additional info
                                        etype, value, tb = sys.exc_info()
                                        try:
                                            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)
                                        finally:
                                            etype, value, tb = None, None, None
                                    except:
                                        traceback.print_exc()

                        if breakpoint.expression is not None:
                            try:
                                try:
                                    val = eval(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)

                        if not main_debugger.first_breakpoint_reached:
                            if event == 'call':
                                if hasattr(frame, 'f_back'):
                                    back = frame.f_back
                                    if back is not None:
                                        # When we start debug session, we call execfile in pydevd run function. It produces an additional
                                        # 'call' event for tracing and we stop on the first line of code twice.
                                        _, back_filename, base = get_abs_path_real_path_and_base_from_frame(
                                            back)
                                        if (base == DEBUG_START[0] and back.f_code.co_name == DEBUG_START[1]) or \
                                                (base == DEBUG_START_PY3K[0] and back.f_code.co_name == DEBUG_START_PY3K[1]):
                                            stop = False
                                            main_debugger.first_breakpoint_reached = True
                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 main_debugger.not_in_scope(
                                filename):
                            # ignore library files while stepping
                            return self.trace_dispatch

                if main_debugger.show_return_values or main_debugger.remove_return_values_flag:
                    self.manage_return_values(main_debugger, frame, event, arg)

                if stop:
                    self.set_suspend(thread, CMD_SET_BREAK)
                    if breakpoint and breakpoint.suspend_policy == "ALL":
                        main_debugger.suspend_all_other_threads(thread)
                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

            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 = event in ('line', '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 not main_debugger.not_in_scope(
                            frame.f_code.co_filename):
                        stop = event == 'line'

                elif step_cmd == CMD_STEP_OVER:
                    stop = stop_frame is frame and event in ('line', 'return')

                    if frame.f_code.co_flags & CO_GENERATOR:
                        if event 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 event == 'line' or event == 'exception':
                        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 = event == 'return' and stop_frame is frame

                elif step_cmd == CMD_RUN_TO_LINE or step_cmd == CMD_SET_NEXT_STATEMENT:
                    stop = False

                    if event == 'line' or event == 'exception':
                        #Yes, we can only act on line events (weird hum?)
                        #Note: This code is duplicated at pydevd.py
                        #Acting on exception events after debugger breaks with exception
                        curr_func_name = frame.f_code.co_name

                        #global context is set with an empty name
                        if curr_func_name in ('?', '<module>'):
                            curr_func_name = ''

                        if curr_func_name == info.pydev_func_name:
                            line = info.pydev_next_line
                            if frame.f_lineno == line:
                                stop = True
                            else:
                                if frame.f_trace is None:
                                    frame.f_trace = self.trace_dispatch
                                frame.f_lineno = line
                                frame.f_trace = None
                                stop = True

                else:
                    stop = False

                if stop and step_cmd != -1 and IS_PY3K:
                    # in Py3k we start script via our custom `execfile` function, and we shouldn't stop there
                    # while stepping when execution is finished
                    if event == 'return' and hasattr(
                            frame, "f_back") and hasattr(
                                frame.f_back, "f_code"):
                        back_filename = os.path.basename(
                            frame.f_back.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 event == '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 == DEBUG_START[
                                    0] and back.f_code.co_name == DEBUG_START[
                                        1]:
                                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

                            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, overwrite_prev_trace=True)
                                    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
                            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 we are quitting, let's stop the tracing
            retVal = None
            if not main_debugger.quitting:
                retVal = self.trace_dispatch

            return retVal
        finally:
            info.is_tracing = False
예제 #26
0
def _is_next_stack_trace_in_project_roots(trace):
    if trace and trace.tb_next and trace.tb_next.tb_frame:
        frame = trace.tb_next.tb_frame
        return in_project_roots(pydevd_file_utils.get_abs_path_real_path_and_base_from_frame(frame)[0])
    return False
예제 #27
0
    def handle_exception(self, frame, event, arg):
        try:
            # print('handle_exception', frame.f_lineno, frame.f_code.co_name)

            # We have 3 things in arg: exception type, description, traceback object
            trace_obj = arg[2]
            main_debugger = self._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:
                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]

                    filename_to_lines_where_exceptions_are_ignored = self.filename_to_lines_where_exceptions_are_ignored

                    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 = self.filename_to_stat_info.get(filename)
                    if last_stat != curr_stat:
                        self.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 = self._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

                main_debugger.send_caught_exception_stack(
                    thread, arg, id(frame))
                self.set_suspend(thread, CMD_STEP_CAUGHT_EXCEPTION)
                self.do_wait_suspend(thread, frame, event, arg)
                main_debugger.send_caught_exception_stack_proceeded(thread)
            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
예제 #28
0
    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', frame.f_lineno, frame.f_code.co_name, 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

            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:
                    flag, frame = self.should_stop_on_exception(frame, event, arg)
                    if flag:
                        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:
                    # I believe this can only happen in jython on some frontiers on jython and java code, which we don't want to trace.
                    return None

            need_trace_return = False
            if is_call and main_debugger.signature_factory:
                need_trace_return = send_signature_call_trace(main_debugger, frame, filename)
            if is_return and main_debugger.signature_factory:
                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 is_return 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_trace_return:
                                return self.trace_return
                            else:
                                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:
                            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>'):
                            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_trace_return:
                                return self.trace_return
                            else:
                                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', frame.f_lineno, frame.f_code.co_name, event)

            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:
                        condition = breakpoint.condition
                        if condition is not None:
                            eval_result = handle_breakpoint_condition(main_debugger, info, breakpoint, new_frame)
                            if not eval_result:
                                return self.trace_dispatch

                        if breakpoint.expression is not None:
                            handle_breakpoint_expression(breakpoint, info, new_frame)

                        if not main_debugger.first_breakpoint_reached:
                            if is_call:
                                back = frame.f_back
                                if back is not None:
                                    # When we start debug session, we call execfile in pydevd run function. It produces an additional
                                    # 'call' event for tracing and we stop on the first line of code twice.
                                    _, back_filename, base = get_abs_path_real_path_and_base_from_frame(back)
                                    if (base == DEBUG_START[0] and back.f_code.co_name == DEBUG_START[1]) or \
                                            (base == DEBUG_START_PY3K[0] and back.f_code.co_name == DEBUG_START_PY3K[1]):
                                        stop = False
                                        main_debugger.first_breakpoint_reached = True
                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 main_debugger.not_in_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)
                    if breakpoint and breakpoint.suspend_policy == "ALL":
                        main_debugger.suspend_all_other_threads(thread)
                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 not is_return:
                        # 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 not main_debugger.not_in_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

                elif step_cmd == CMD_RUN_TO_LINE or step_cmd == CMD_SET_NEXT_STATEMENT:
                    try:
                        stop, _, response_msg = main_debugger.set_next_statement(frame, event, info.pydev_func_name, info.pydev_next_line)
                    except ValueError:
                        pass

                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 == DEBUG_START[0] and back.f_code.co_name == DEBUG_START[1]:
                                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

                            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, overwrite_prev_trace=True)
                                    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
                            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 we are quitting, let's stop the tracing
            retVal = None
            if not main_debugger.quitting:
                retVal = self.trace_dispatch

            return retVal
        finally:
            info.is_tracing = False
예제 #29
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
예제 #30
0
def _schedule_callback(prev, next):
    '''
    Called when a context is stopped or a new context is made runnable.
    '''
    try:
        if not prev and not next:
            return

        current_frame = sys._getframe()

        if next:
            register_tasklet_info(next)

            # Ok, making next runnable: set the tracing facility in it.
            debugger = get_global_debugger()
            if debugger is not None:
                next.trace_function = debugger.get_thread_local_trace_func()
                frame = next.frame
                if frame is current_frame:
                    frame = frame.f_back
                if hasattr(frame, 'f_trace'):  # Note: can be None (but hasattr should cover for that too).
                    frame.f_trace = debugger.get_thread_local_trace_func()

            debugger = None

        if prev:
            register_tasklet_info(prev)

        try:
            for tasklet_ref, tasklet_info in dict_items(_weak_tasklet_registered_to_info):  # Make sure it's a copy!
                tasklet = tasklet_ref()
                if tasklet is None or not tasklet.alive:
                    # Garbage-collected already!
                    try:
                        del _weak_tasklet_registered_to_info[tasklet_ref]
                    except KeyError:
                        pass
                    if tasklet_info.frame_id is not None:
                        remove_custom_frame(tasklet_info.frame_id)
                else:
                    is_running = stackless.get_thread_info(tasklet.thread_id)[1] is tasklet
                    if tasklet is prev or (tasklet is not next and not is_running):
                        # the tasklet won't run after this scheduler action:
                        # - the tasklet is the previous tasklet
                        # - it is not the next tasklet and it is not an already running tasklet
                        frame = tasklet.frame
                        if frame is current_frame:
                            frame = frame.f_back
                        if frame is not None:
                            abs_real_path_and_base = get_abs_path_real_path_and_base_from_frame(frame)
                            # print >>sys.stderr, "SchedCB: %r, %d, '%s', '%s'" % (tasklet, frame.f_lineno, _filename, base)
                            if debugger.get_file_type(abs_real_path_and_base) is None:
                                tasklet_info.update_name()
                                if tasklet_info.frame_id is None:
                                    tasklet_info.frame_id = add_custom_frame(frame, tasklet_info.tasklet_name, tasklet.thread_id)
                                else:
                                    update_custom_frame(tasklet_info.frame_id, frame, tasklet.thread_id, name=tasklet_info.tasklet_name)

                    elif tasklet is next or is_running:
                        if tasklet_info.frame_id is not None:
                            # Remove info about stackless suspended when it starts to run.
                            remove_custom_frame(tasklet_info.frame_id)
                            tasklet_info.frame_id = None


        finally:
            tasklet = None
            tasklet_info = None
            frame = None

    except:
        import traceback;traceback.print_exc()

    if _application_set_schedule_callback is not None:
        return _application_set_schedule_callback(prev, next)
예제 #31
0
    def __call__(self, frame, event, arg):
        ''' This is the callback used when we enter some context in the debugger.

        We also decorate the thread we are in with info about the debugging.
        The attributes added are:
            pydev_state
            pydev_step_stop
            pydev_step_cmd
            pydev_notify_kill

        :param PyDB py_db:
            This is the global debugger (this method should actually be added as a method to it).
        '''
        # IFDEF CYTHON
        # cdef str filename;
        # cdef str base;
        # cdef int pydev_step_cmd;
        # cdef tuple cache_key;
        # cdef dict cache_skips;
        # cdef bint is_stepping;
        # cdef tuple abs_path_real_path_and_base;
        # cdef PyDBAdditionalThreadInfo additional_info;
        # ENDIF
        # print('ENTER: trace_dispatch', frame.f_code.co_filename, frame.f_lineno, event, frame.f_code.co_name)
        py_db, t, additional_info, cache_skips, frame_skips_cache = self._args
        pydev_step_cmd = additional_info.pydev_step_cmd
        is_stepping = pydev_step_cmd != -1

        try:
            if py_db._finish_debugging_session:
                if not py_db._termination_event_set:
                    # that was not working very well because jython gave some socket errors
                    try:
                        if py_db.output_checker is None:
                            kill_all_pydev_threads()
                    except:
                        traceback.print_exc()
                    py_db._termination_event_set = True
                return None

            # if thread is not alive, cancel trace_dispatch processing
            if not is_thread_alive(t):
                py_db._process_thread_not_alive(get_thread_id(t))
                return None  # suspend tracing

            try:
                # Make fast path faster!
                abs_path_real_path_and_base = NORM_PATHS_AND_BASE_CONTAINER[
                    frame.f_code.co_filename]
            except:
                abs_path_real_path_and_base = get_abs_path_real_path_and_base_from_frame(
                    frame)

            if py_db.thread_analyser is not None:
                py_db.thread_analyser.log_event(frame)

            if py_db.asyncio_analyser is not None:
                py_db.asyncio_analyser.log_event(frame)

            filename = abs_path_real_path_and_base[1]
            # Note: it's important that the context name is also given because we may hit something once
            # in the global context and another in the local context.
            cache_key = (frame.f_lineno, frame.f_code.co_name, filename)
            if not is_stepping and cache_key in cache_skips:
                # print('skipped: trace_dispatch (cache hit)', cache_key, frame.f_lineno, event, frame.f_code.co_name)
                return None

            file_type = get_file_type(
                abs_path_real_path_and_base[-1]
            )  # we don't want to debug threading or anything related to pydevd

            if file_type is not None:
                if file_type == 1:  # inlining LIB_FILE = 1
                    if py_db.not_in_scope(filename):
                        # print('skipped: trace_dispatch (not in scope)', abs_path_real_path_and_base[-1], frame.f_lineno, event, frame.f_code.co_name, file_type)
                        cache_skips[cache_key] = 1
                        return None
                else:
                    # print('skipped: trace_dispatch', abs_path_real_path_and_base[-1], frame.f_lineno, event, frame.f_code.co_name, file_type)
                    cache_skips[cache_key] = 1
                    return None

            if is_stepping:
                if py_db.is_filter_enabled and py_db.is_ignored_by_filters(
                        filename):
                    # ignore files matching stepping filters
                    return None
                if py_db.is_filter_libraries and py_db.not_in_scope(filename):
                    # ignore library files while stepping
                    return None

            # print('trace_dispatch', base, frame.f_lineno, event, frame.f_code.co_name, file_type)
            if additional_info.is_tracing:
                return None  # we don't wan't to trace code invoked from pydevd_frame.trace_dispatch

            # Just create PyDBFrame directly (removed support for Python versions < 2.5, which required keeping a weak
            # reference to the frame).
            ret = PyDBFrame(
                (py_db, filename, additional_info, t, frame_skips_cache,
                 (frame.f_code.co_name, frame.f_code.co_firstlineno,
                  filename))).trace_dispatch(frame, event, arg)
            if ret is None:
                cache_skips[cache_key] = 1
                return None

            # IFDEF CYTHON
            # return SafeCallWrapper(ret)
            # ELSE
            return ret
            # ENDIF

        except SystemExit:
            return None

        except Exception:
            if py_db._finish_debugging_session:
                return None  # Don't log errors when we're shutting down.
            # Log it
            try:
                if traceback is not None:
                    # This can actually happen during the interpreter shutdown in Python 2.7
                    traceback.print_exc()
            except:
                # Error logging? We're really in the interpreter shutdown...
                # (https://github.com/fabioz/PyDev.Debugger/issues/8)
                pass
            return None
예제 #32
0
    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 = self.should_stop_on_exception(
                        frame, event, arg)
                    if should_stop:
                        self.handle_exception(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, self, 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 ('?', '<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:
                            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:
                    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, 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 = 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 ('<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.

                        # 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:
                    self.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 (
                                '?', '<module>') 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
예제 #33
0
def is_test_item_or_set_up_caller(trace):
    """Check if the frame is the test item or set up caller.

    A test function caller is a function that calls actual test code which can be, for example,
    `unittest.TestCase` test method or function `pytest` assumes to be a test. A caller function
    is the one we want to trace to catch failed test events. Tracing test functions
    themselves is not possible because some exceptions can be caught in the test code, and
    we are interested only in exceptions that are propagated to the test framework level.
    """
    if not trace:
        return False

    frame = trace.tb_frame

    abs_path, _, _ = pydevd_file_utils.get_abs_path_real_path_and_base_from_frame(
        frame)
    if in_project_roots(abs_path):
        # We are interested in exceptions made it to the test framework scope.
        return False

    if not trace.tb_next:
        # This can happen when the exception has been raised inside a test item or set up caller.
        return False

    if not _is_next_stack_trace_in_project_roots(trace):
        # The next stack frame must be the frame of a project scope function, otherwise we risk stopping
        # at a line a few times since multiple test framework functions we are looking for may appear in the stack.
        return False

    # Set up and tear down methods can be checked immediately, since they are shared by both `pytest` and `unittest`.
    unittest_set_up_and_tear_down_methods = ('_callSetUp', '_callTearDown')
    if frame.f_code.co_name in unittest_set_up_and_tear_down_methods:
        return True

    # It is important to check if the tests are run with `pytest` first because it can run `unittest` code
    # internally. This may lead to stopping on  broken tests twice: one in the `pytest` test runner
    # and second in the `unittest` runner.
    is_pytest = False

    f = frame
    while f:
        # noinspection SpellCheckingInspection
        if f.f_code.co_name == 'pytest_cmdline_main':
            is_pytest = True
        f = f.f_back

    unittest_caller_names = ['_callTestMethod', 'runTest', 'run']
    if IS_PY3K:
        unittest_caller_names.append('subTest')

    if is_pytest:
        # noinspection SpellCheckingInspection
        if frame.f_code.co_name in ('pytest_pyfunc_call', 'call_fixture_func',
                                    '_eval_scope_callable',
                                    '_teardown_yield_fixture'):
            return True
        else:
            return frame.f_code.co_name in unittest_caller_names

    else:
        import unittest
        test_case_obj = frame.f_locals.get('self')

        # Check for `_FailedTest` is important to detect cases when tests cannot be run on the first place,
        # e.g. there was an import error in the test module. Can happen both in Python 3.8 and earlier versions.
        if isinstance(
                test_case_obj,
                getattr(getattr(unittest, 'loader', None), '_FailedTest',
                        None)):
            return False

        if frame.f_code.co_name in unittest_caller_names:
            # unittest and nose
            return True

    return False