17db96d56Sopenharmony_ci"""Debugger basics"""
27db96d56Sopenharmony_ci
37db96d56Sopenharmony_ciimport fnmatch
47db96d56Sopenharmony_ciimport sys
57db96d56Sopenharmony_ciimport os
67db96d56Sopenharmony_cifrom inspect import CO_GENERATOR, CO_COROUTINE, CO_ASYNC_GENERATOR
77db96d56Sopenharmony_ci
87db96d56Sopenharmony_ci__all__ = ["BdbQuit", "Bdb", "Breakpoint"]
97db96d56Sopenharmony_ci
107db96d56Sopenharmony_ciGENERATOR_AND_COROUTINE_FLAGS = CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR
117db96d56Sopenharmony_ci
127db96d56Sopenharmony_ci
137db96d56Sopenharmony_ciclass BdbQuit(Exception):
147db96d56Sopenharmony_ci    """Exception to give up completely."""
157db96d56Sopenharmony_ci
167db96d56Sopenharmony_ci
177db96d56Sopenharmony_ciclass Bdb:
187db96d56Sopenharmony_ci    """Generic Python debugger base class.
197db96d56Sopenharmony_ci
207db96d56Sopenharmony_ci    This class takes care of details of the trace facility;
217db96d56Sopenharmony_ci    a derived class should implement user interaction.
227db96d56Sopenharmony_ci    The standard debugger class (pdb.Pdb) is an example.
237db96d56Sopenharmony_ci
247db96d56Sopenharmony_ci    The optional skip argument must be an iterable of glob-style
257db96d56Sopenharmony_ci    module name patterns.  The debugger will not step into frames
267db96d56Sopenharmony_ci    that originate in a module that matches one of these patterns.
277db96d56Sopenharmony_ci    Whether a frame is considered to originate in a certain module
287db96d56Sopenharmony_ci    is determined by the __name__ in the frame globals.
297db96d56Sopenharmony_ci    """
307db96d56Sopenharmony_ci
317db96d56Sopenharmony_ci    def __init__(self, skip=None):
327db96d56Sopenharmony_ci        self.skip = set(skip) if skip else None
337db96d56Sopenharmony_ci        self.breaks = {}
347db96d56Sopenharmony_ci        self.fncache = {}
357db96d56Sopenharmony_ci        self.frame_returning = None
367db96d56Sopenharmony_ci
377db96d56Sopenharmony_ci        self._load_breaks()
387db96d56Sopenharmony_ci
397db96d56Sopenharmony_ci    def canonic(self, filename):
407db96d56Sopenharmony_ci        """Return canonical form of filename.
417db96d56Sopenharmony_ci
427db96d56Sopenharmony_ci        For real filenames, the canonical form is a case-normalized (on
437db96d56Sopenharmony_ci        case insensitive filesystems) absolute path.  'Filenames' with
447db96d56Sopenharmony_ci        angle brackets, such as "<stdin>", generated in interactive
457db96d56Sopenharmony_ci        mode, are returned unchanged.
467db96d56Sopenharmony_ci        """
477db96d56Sopenharmony_ci        if filename == "<" + filename[1:-1] + ">":
487db96d56Sopenharmony_ci            return filename
497db96d56Sopenharmony_ci        canonic = self.fncache.get(filename)
507db96d56Sopenharmony_ci        if not canonic:
517db96d56Sopenharmony_ci            canonic = os.path.abspath(filename)
527db96d56Sopenharmony_ci            canonic = os.path.normcase(canonic)
537db96d56Sopenharmony_ci            self.fncache[filename] = canonic
547db96d56Sopenharmony_ci        return canonic
557db96d56Sopenharmony_ci
567db96d56Sopenharmony_ci    def reset(self):
577db96d56Sopenharmony_ci        """Set values of attributes as ready to start debugging."""
587db96d56Sopenharmony_ci        import linecache
597db96d56Sopenharmony_ci        linecache.checkcache()
607db96d56Sopenharmony_ci        self.botframe = None
617db96d56Sopenharmony_ci        self._set_stopinfo(None, None)
627db96d56Sopenharmony_ci
637db96d56Sopenharmony_ci    def trace_dispatch(self, frame, event, arg):
647db96d56Sopenharmony_ci        """Dispatch a trace function for debugged frames based on the event.
657db96d56Sopenharmony_ci
667db96d56Sopenharmony_ci        This function is installed as the trace function for debugged
677db96d56Sopenharmony_ci        frames. Its return value is the new trace function, which is
687db96d56Sopenharmony_ci        usually itself. The default implementation decides how to
697db96d56Sopenharmony_ci        dispatch a frame, depending on the type of event (passed in as a
707db96d56Sopenharmony_ci        string) that is about to be executed.
717db96d56Sopenharmony_ci
727db96d56Sopenharmony_ci        The event can be one of the following:
737db96d56Sopenharmony_ci            line: A new line of code is going to be executed.
747db96d56Sopenharmony_ci            call: A function is about to be called or another code block
757db96d56Sopenharmony_ci                  is entered.
767db96d56Sopenharmony_ci            return: A function or other code block is about to return.
777db96d56Sopenharmony_ci            exception: An exception has occurred.
787db96d56Sopenharmony_ci            c_call: A C function is about to be called.
797db96d56Sopenharmony_ci            c_return: A C function has returned.
807db96d56Sopenharmony_ci            c_exception: A C function has raised an exception.
817db96d56Sopenharmony_ci
827db96d56Sopenharmony_ci        For the Python events, specialized functions (see the dispatch_*()
837db96d56Sopenharmony_ci        methods) are called.  For the C events, no action is taken.
847db96d56Sopenharmony_ci
857db96d56Sopenharmony_ci        The arg parameter depends on the previous event.
867db96d56Sopenharmony_ci        """
877db96d56Sopenharmony_ci        if self.quitting:
887db96d56Sopenharmony_ci            return # None
897db96d56Sopenharmony_ci        if event == 'line':
907db96d56Sopenharmony_ci            return self.dispatch_line(frame)
917db96d56Sopenharmony_ci        if event == 'call':
927db96d56Sopenharmony_ci            return self.dispatch_call(frame, arg)
937db96d56Sopenharmony_ci        if event == 'return':
947db96d56Sopenharmony_ci            return self.dispatch_return(frame, arg)
957db96d56Sopenharmony_ci        if event == 'exception':
967db96d56Sopenharmony_ci            return self.dispatch_exception(frame, arg)
977db96d56Sopenharmony_ci        if event == 'c_call':
987db96d56Sopenharmony_ci            return self.trace_dispatch
997db96d56Sopenharmony_ci        if event == 'c_exception':
1007db96d56Sopenharmony_ci            return self.trace_dispatch
1017db96d56Sopenharmony_ci        if event == 'c_return':
1027db96d56Sopenharmony_ci            return self.trace_dispatch
1037db96d56Sopenharmony_ci        print('bdb.Bdb.dispatch: unknown debugging event:', repr(event))
1047db96d56Sopenharmony_ci        return self.trace_dispatch
1057db96d56Sopenharmony_ci
1067db96d56Sopenharmony_ci    def dispatch_line(self, frame):
1077db96d56Sopenharmony_ci        """Invoke user function and return trace function for line event.
1087db96d56Sopenharmony_ci
1097db96d56Sopenharmony_ci        If the debugger stops on the current line, invoke
1107db96d56Sopenharmony_ci        self.user_line(). Raise BdbQuit if self.quitting is set.
1117db96d56Sopenharmony_ci        Return self.trace_dispatch to continue tracing in this scope.
1127db96d56Sopenharmony_ci        """
1137db96d56Sopenharmony_ci        if self.stop_here(frame) or self.break_here(frame):
1147db96d56Sopenharmony_ci            self.user_line(frame)
1157db96d56Sopenharmony_ci            if self.quitting: raise BdbQuit
1167db96d56Sopenharmony_ci        return self.trace_dispatch
1177db96d56Sopenharmony_ci
1187db96d56Sopenharmony_ci    def dispatch_call(self, frame, arg):
1197db96d56Sopenharmony_ci        """Invoke user function and return trace function for call event.
1207db96d56Sopenharmony_ci
1217db96d56Sopenharmony_ci        If the debugger stops on this function call, invoke
1227db96d56Sopenharmony_ci        self.user_call(). Raise BdbQuit if self.quitting is set.
1237db96d56Sopenharmony_ci        Return self.trace_dispatch to continue tracing in this scope.
1247db96d56Sopenharmony_ci        """
1257db96d56Sopenharmony_ci        # XXX 'arg' is no longer used
1267db96d56Sopenharmony_ci        if self.botframe is None:
1277db96d56Sopenharmony_ci            # First call of dispatch since reset()
1287db96d56Sopenharmony_ci            self.botframe = frame.f_back # (CT) Note that this may also be None!
1297db96d56Sopenharmony_ci            return self.trace_dispatch
1307db96d56Sopenharmony_ci        if not (self.stop_here(frame) or self.break_anywhere(frame)):
1317db96d56Sopenharmony_ci            # No need to trace this function
1327db96d56Sopenharmony_ci            return # None
1337db96d56Sopenharmony_ci        # Ignore call events in generator except when stepping.
1347db96d56Sopenharmony_ci        if self.stopframe and frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS:
1357db96d56Sopenharmony_ci            return self.trace_dispatch
1367db96d56Sopenharmony_ci        self.user_call(frame, arg)
1377db96d56Sopenharmony_ci        if self.quitting: raise BdbQuit
1387db96d56Sopenharmony_ci        return self.trace_dispatch
1397db96d56Sopenharmony_ci
1407db96d56Sopenharmony_ci    def dispatch_return(self, frame, arg):
1417db96d56Sopenharmony_ci        """Invoke user function and return trace function for return event.
1427db96d56Sopenharmony_ci
1437db96d56Sopenharmony_ci        If the debugger stops on this function return, invoke
1447db96d56Sopenharmony_ci        self.user_return(). Raise BdbQuit if self.quitting is set.
1457db96d56Sopenharmony_ci        Return self.trace_dispatch to continue tracing in this scope.
1467db96d56Sopenharmony_ci        """
1477db96d56Sopenharmony_ci        if self.stop_here(frame) or frame == self.returnframe:
1487db96d56Sopenharmony_ci            # Ignore return events in generator except when stepping.
1497db96d56Sopenharmony_ci            if self.stopframe and frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS:
1507db96d56Sopenharmony_ci                return self.trace_dispatch
1517db96d56Sopenharmony_ci            try:
1527db96d56Sopenharmony_ci                self.frame_returning = frame
1537db96d56Sopenharmony_ci                self.user_return(frame, arg)
1547db96d56Sopenharmony_ci            finally:
1557db96d56Sopenharmony_ci                self.frame_returning = None
1567db96d56Sopenharmony_ci            if self.quitting: raise BdbQuit
1577db96d56Sopenharmony_ci            # The user issued a 'next' or 'until' command.
1587db96d56Sopenharmony_ci            if self.stopframe is frame and self.stoplineno != -1:
1597db96d56Sopenharmony_ci                self._set_stopinfo(None, None)
1607db96d56Sopenharmony_ci        return self.trace_dispatch
1617db96d56Sopenharmony_ci
1627db96d56Sopenharmony_ci    def dispatch_exception(self, frame, arg):
1637db96d56Sopenharmony_ci        """Invoke user function and return trace function for exception event.
1647db96d56Sopenharmony_ci
1657db96d56Sopenharmony_ci        If the debugger stops on this exception, invoke
1667db96d56Sopenharmony_ci        self.user_exception(). Raise BdbQuit if self.quitting is set.
1677db96d56Sopenharmony_ci        Return self.trace_dispatch to continue tracing in this scope.
1687db96d56Sopenharmony_ci        """
1697db96d56Sopenharmony_ci        if self.stop_here(frame):
1707db96d56Sopenharmony_ci            # When stepping with next/until/return in a generator frame, skip
1717db96d56Sopenharmony_ci            # the internal StopIteration exception (with no traceback)
1727db96d56Sopenharmony_ci            # triggered by a subiterator run with the 'yield from' statement.
1737db96d56Sopenharmony_ci            if not (frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS
1747db96d56Sopenharmony_ci                    and arg[0] is StopIteration and arg[2] is None):
1757db96d56Sopenharmony_ci                self.user_exception(frame, arg)
1767db96d56Sopenharmony_ci                if self.quitting: raise BdbQuit
1777db96d56Sopenharmony_ci        # Stop at the StopIteration or GeneratorExit exception when the user
1787db96d56Sopenharmony_ci        # has set stopframe in a generator by issuing a return command, or a
1797db96d56Sopenharmony_ci        # next/until command at the last statement in the generator before the
1807db96d56Sopenharmony_ci        # exception.
1817db96d56Sopenharmony_ci        elif (self.stopframe and frame is not self.stopframe
1827db96d56Sopenharmony_ci                and self.stopframe.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS
1837db96d56Sopenharmony_ci                and arg[0] in (StopIteration, GeneratorExit)):
1847db96d56Sopenharmony_ci            self.user_exception(frame, arg)
1857db96d56Sopenharmony_ci            if self.quitting: raise BdbQuit
1867db96d56Sopenharmony_ci
1877db96d56Sopenharmony_ci        return self.trace_dispatch
1887db96d56Sopenharmony_ci
1897db96d56Sopenharmony_ci    # Normally derived classes don't override the following
1907db96d56Sopenharmony_ci    # methods, but they may if they want to redefine the
1917db96d56Sopenharmony_ci    # definition of stopping and breakpoints.
1927db96d56Sopenharmony_ci
1937db96d56Sopenharmony_ci    def is_skipped_module(self, module_name):
1947db96d56Sopenharmony_ci        "Return True if module_name matches any skip pattern."
1957db96d56Sopenharmony_ci        if module_name is None:  # some modules do not have names
1967db96d56Sopenharmony_ci            return False
1977db96d56Sopenharmony_ci        for pattern in self.skip:
1987db96d56Sopenharmony_ci            if fnmatch.fnmatch(module_name, pattern):
1997db96d56Sopenharmony_ci                return True
2007db96d56Sopenharmony_ci        return False
2017db96d56Sopenharmony_ci
2027db96d56Sopenharmony_ci    def stop_here(self, frame):
2037db96d56Sopenharmony_ci        "Return True if frame is below the starting frame in the stack."
2047db96d56Sopenharmony_ci        # (CT) stopframe may now also be None, see dispatch_call.
2057db96d56Sopenharmony_ci        # (CT) the former test for None is therefore removed from here.
2067db96d56Sopenharmony_ci        if self.skip and \
2077db96d56Sopenharmony_ci               self.is_skipped_module(frame.f_globals.get('__name__')):
2087db96d56Sopenharmony_ci            return False
2097db96d56Sopenharmony_ci        if frame is self.stopframe:
2107db96d56Sopenharmony_ci            if self.stoplineno == -1:
2117db96d56Sopenharmony_ci                return False
2127db96d56Sopenharmony_ci            return frame.f_lineno >= self.stoplineno
2137db96d56Sopenharmony_ci        if not self.stopframe:
2147db96d56Sopenharmony_ci            return True
2157db96d56Sopenharmony_ci        return False
2167db96d56Sopenharmony_ci
2177db96d56Sopenharmony_ci    def break_here(self, frame):
2187db96d56Sopenharmony_ci        """Return True if there is an effective breakpoint for this line.
2197db96d56Sopenharmony_ci
2207db96d56Sopenharmony_ci        Check for line or function breakpoint and if in effect.
2217db96d56Sopenharmony_ci        Delete temporary breakpoints if effective() says to.
2227db96d56Sopenharmony_ci        """
2237db96d56Sopenharmony_ci        filename = self.canonic(frame.f_code.co_filename)
2247db96d56Sopenharmony_ci        if filename not in self.breaks:
2257db96d56Sopenharmony_ci            return False
2267db96d56Sopenharmony_ci        lineno = frame.f_lineno
2277db96d56Sopenharmony_ci        if lineno not in self.breaks[filename]:
2287db96d56Sopenharmony_ci            # The line itself has no breakpoint, but maybe the line is the
2297db96d56Sopenharmony_ci            # first line of a function with breakpoint set by function name.
2307db96d56Sopenharmony_ci            lineno = frame.f_code.co_firstlineno
2317db96d56Sopenharmony_ci            if lineno not in self.breaks[filename]:
2327db96d56Sopenharmony_ci                return False
2337db96d56Sopenharmony_ci
2347db96d56Sopenharmony_ci        # flag says ok to delete temp. bp
2357db96d56Sopenharmony_ci        (bp, flag) = effective(filename, lineno, frame)
2367db96d56Sopenharmony_ci        if bp:
2377db96d56Sopenharmony_ci            self.currentbp = bp.number
2387db96d56Sopenharmony_ci            if (flag and bp.temporary):
2397db96d56Sopenharmony_ci                self.do_clear(str(bp.number))
2407db96d56Sopenharmony_ci            return True
2417db96d56Sopenharmony_ci        else:
2427db96d56Sopenharmony_ci            return False
2437db96d56Sopenharmony_ci
2447db96d56Sopenharmony_ci    def do_clear(self, arg):
2457db96d56Sopenharmony_ci        """Remove temporary breakpoint.
2467db96d56Sopenharmony_ci
2477db96d56Sopenharmony_ci        Must implement in derived classes or get NotImplementedError.
2487db96d56Sopenharmony_ci        """
2497db96d56Sopenharmony_ci        raise NotImplementedError("subclass of bdb must implement do_clear()")
2507db96d56Sopenharmony_ci
2517db96d56Sopenharmony_ci    def break_anywhere(self, frame):
2527db96d56Sopenharmony_ci        """Return True if there is any breakpoint for frame's filename.
2537db96d56Sopenharmony_ci        """
2547db96d56Sopenharmony_ci        return self.canonic(frame.f_code.co_filename) in self.breaks
2557db96d56Sopenharmony_ci
2567db96d56Sopenharmony_ci    # Derived classes should override the user_* methods
2577db96d56Sopenharmony_ci    # to gain control.
2587db96d56Sopenharmony_ci
2597db96d56Sopenharmony_ci    def user_call(self, frame, argument_list):
2607db96d56Sopenharmony_ci        """Called if we might stop in a function."""
2617db96d56Sopenharmony_ci        pass
2627db96d56Sopenharmony_ci
2637db96d56Sopenharmony_ci    def user_line(self, frame):
2647db96d56Sopenharmony_ci        """Called when we stop or break at a line."""
2657db96d56Sopenharmony_ci        pass
2667db96d56Sopenharmony_ci
2677db96d56Sopenharmony_ci    def user_return(self, frame, return_value):
2687db96d56Sopenharmony_ci        """Called when a return trap is set here."""
2697db96d56Sopenharmony_ci        pass
2707db96d56Sopenharmony_ci
2717db96d56Sopenharmony_ci    def user_exception(self, frame, exc_info):
2727db96d56Sopenharmony_ci        """Called when we stop on an exception."""
2737db96d56Sopenharmony_ci        pass
2747db96d56Sopenharmony_ci
2757db96d56Sopenharmony_ci    def _set_stopinfo(self, stopframe, returnframe, stoplineno=0):
2767db96d56Sopenharmony_ci        """Set the attributes for stopping.
2777db96d56Sopenharmony_ci
2787db96d56Sopenharmony_ci        If stoplineno is greater than or equal to 0, then stop at line
2797db96d56Sopenharmony_ci        greater than or equal to the stopline.  If stoplineno is -1, then
2807db96d56Sopenharmony_ci        don't stop at all.
2817db96d56Sopenharmony_ci        """
2827db96d56Sopenharmony_ci        self.stopframe = stopframe
2837db96d56Sopenharmony_ci        self.returnframe = returnframe
2847db96d56Sopenharmony_ci        self.quitting = False
2857db96d56Sopenharmony_ci        # stoplineno >= 0 means: stop at line >= the stoplineno
2867db96d56Sopenharmony_ci        # stoplineno -1 means: don't stop at all
2877db96d56Sopenharmony_ci        self.stoplineno = stoplineno
2887db96d56Sopenharmony_ci
2897db96d56Sopenharmony_ci    # Derived classes and clients can call the following methods
2907db96d56Sopenharmony_ci    # to affect the stepping state.
2917db96d56Sopenharmony_ci
2927db96d56Sopenharmony_ci    def set_until(self, frame, lineno=None):
2937db96d56Sopenharmony_ci        """Stop when the line with the lineno greater than the current one is
2947db96d56Sopenharmony_ci        reached or when returning from current frame."""
2957db96d56Sopenharmony_ci        # the name "until" is borrowed from gdb
2967db96d56Sopenharmony_ci        if lineno is None:
2977db96d56Sopenharmony_ci            lineno = frame.f_lineno + 1
2987db96d56Sopenharmony_ci        self._set_stopinfo(frame, frame, lineno)
2997db96d56Sopenharmony_ci
3007db96d56Sopenharmony_ci    def set_step(self):
3017db96d56Sopenharmony_ci        """Stop after one line of code."""
3027db96d56Sopenharmony_ci        # Issue #13183: pdb skips frames after hitting a breakpoint and running
3037db96d56Sopenharmony_ci        # step commands.
3047db96d56Sopenharmony_ci        # Restore the trace function in the caller (that may not have been set
3057db96d56Sopenharmony_ci        # for performance reasons) when returning from the current frame.
3067db96d56Sopenharmony_ci        if self.frame_returning:
3077db96d56Sopenharmony_ci            caller_frame = self.frame_returning.f_back
3087db96d56Sopenharmony_ci            if caller_frame and not caller_frame.f_trace:
3097db96d56Sopenharmony_ci                caller_frame.f_trace = self.trace_dispatch
3107db96d56Sopenharmony_ci        self._set_stopinfo(None, None)
3117db96d56Sopenharmony_ci
3127db96d56Sopenharmony_ci    def set_next(self, frame):
3137db96d56Sopenharmony_ci        """Stop on the next line in or below the given frame."""
3147db96d56Sopenharmony_ci        self._set_stopinfo(frame, None)
3157db96d56Sopenharmony_ci
3167db96d56Sopenharmony_ci    def set_return(self, frame):
3177db96d56Sopenharmony_ci        """Stop when returning from the given frame."""
3187db96d56Sopenharmony_ci        if frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS:
3197db96d56Sopenharmony_ci            self._set_stopinfo(frame, None, -1)
3207db96d56Sopenharmony_ci        else:
3217db96d56Sopenharmony_ci            self._set_stopinfo(frame.f_back, frame)
3227db96d56Sopenharmony_ci
3237db96d56Sopenharmony_ci    def set_trace(self, frame=None):
3247db96d56Sopenharmony_ci        """Start debugging from frame.
3257db96d56Sopenharmony_ci
3267db96d56Sopenharmony_ci        If frame is not specified, debugging starts from caller's frame.
3277db96d56Sopenharmony_ci        """
3287db96d56Sopenharmony_ci        if frame is None:
3297db96d56Sopenharmony_ci            frame = sys._getframe().f_back
3307db96d56Sopenharmony_ci        self.reset()
3317db96d56Sopenharmony_ci        while frame:
3327db96d56Sopenharmony_ci            frame.f_trace = self.trace_dispatch
3337db96d56Sopenharmony_ci            self.botframe = frame
3347db96d56Sopenharmony_ci            frame = frame.f_back
3357db96d56Sopenharmony_ci        self.set_step()
3367db96d56Sopenharmony_ci        sys.settrace(self.trace_dispatch)
3377db96d56Sopenharmony_ci
3387db96d56Sopenharmony_ci    def set_continue(self):
3397db96d56Sopenharmony_ci        """Stop only at breakpoints or when finished.
3407db96d56Sopenharmony_ci
3417db96d56Sopenharmony_ci        If there are no breakpoints, set the system trace function to None.
3427db96d56Sopenharmony_ci        """
3437db96d56Sopenharmony_ci        # Don't stop except at breakpoints or when finished
3447db96d56Sopenharmony_ci        self._set_stopinfo(self.botframe, None, -1)
3457db96d56Sopenharmony_ci        if not self.breaks:
3467db96d56Sopenharmony_ci            # no breakpoints; run without debugger overhead
3477db96d56Sopenharmony_ci            sys.settrace(None)
3487db96d56Sopenharmony_ci            frame = sys._getframe().f_back
3497db96d56Sopenharmony_ci            while frame and frame is not self.botframe:
3507db96d56Sopenharmony_ci                del frame.f_trace
3517db96d56Sopenharmony_ci                frame = frame.f_back
3527db96d56Sopenharmony_ci
3537db96d56Sopenharmony_ci    def set_quit(self):
3547db96d56Sopenharmony_ci        """Set quitting attribute to True.
3557db96d56Sopenharmony_ci
3567db96d56Sopenharmony_ci        Raises BdbQuit exception in the next call to a dispatch_*() method.
3577db96d56Sopenharmony_ci        """
3587db96d56Sopenharmony_ci        self.stopframe = self.botframe
3597db96d56Sopenharmony_ci        self.returnframe = None
3607db96d56Sopenharmony_ci        self.quitting = True
3617db96d56Sopenharmony_ci        sys.settrace(None)
3627db96d56Sopenharmony_ci
3637db96d56Sopenharmony_ci    # Derived classes and clients can call the following methods
3647db96d56Sopenharmony_ci    # to manipulate breakpoints.  These methods return an
3657db96d56Sopenharmony_ci    # error message if something went wrong, None if all is well.
3667db96d56Sopenharmony_ci    # Set_break prints out the breakpoint line and file:lineno.
3677db96d56Sopenharmony_ci    # Call self.get_*break*() to see the breakpoints or better
3687db96d56Sopenharmony_ci    # for bp in Breakpoint.bpbynumber: if bp: bp.bpprint().
3697db96d56Sopenharmony_ci
3707db96d56Sopenharmony_ci    def _add_to_breaks(self, filename, lineno):
3717db96d56Sopenharmony_ci        """Add breakpoint to breaks, if not already there."""
3727db96d56Sopenharmony_ci        bp_linenos = self.breaks.setdefault(filename, [])
3737db96d56Sopenharmony_ci        if lineno not in bp_linenos:
3747db96d56Sopenharmony_ci            bp_linenos.append(lineno)
3757db96d56Sopenharmony_ci
3767db96d56Sopenharmony_ci    def set_break(self, filename, lineno, temporary=False, cond=None,
3777db96d56Sopenharmony_ci                  funcname=None):
3787db96d56Sopenharmony_ci        """Set a new breakpoint for filename:lineno.
3797db96d56Sopenharmony_ci
3807db96d56Sopenharmony_ci        If lineno doesn't exist for the filename, return an error message.
3817db96d56Sopenharmony_ci        The filename should be in canonical form.
3827db96d56Sopenharmony_ci        """
3837db96d56Sopenharmony_ci        filename = self.canonic(filename)
3847db96d56Sopenharmony_ci        import linecache # Import as late as possible
3857db96d56Sopenharmony_ci        line = linecache.getline(filename, lineno)
3867db96d56Sopenharmony_ci        if not line:
3877db96d56Sopenharmony_ci            return 'Line %s:%d does not exist' % (filename, lineno)
3887db96d56Sopenharmony_ci        self._add_to_breaks(filename, lineno)
3897db96d56Sopenharmony_ci        bp = Breakpoint(filename, lineno, temporary, cond, funcname)
3907db96d56Sopenharmony_ci        return None
3917db96d56Sopenharmony_ci
3927db96d56Sopenharmony_ci    def _load_breaks(self):
3937db96d56Sopenharmony_ci        """Apply all breakpoints (set in other instances) to this one.
3947db96d56Sopenharmony_ci
3957db96d56Sopenharmony_ci        Populates this instance's breaks list from the Breakpoint class's
3967db96d56Sopenharmony_ci        list, which can have breakpoints set by another Bdb instance. This
3977db96d56Sopenharmony_ci        is necessary for interactive sessions to keep the breakpoints
3987db96d56Sopenharmony_ci        active across multiple calls to run().
3997db96d56Sopenharmony_ci        """
4007db96d56Sopenharmony_ci        for (filename, lineno) in Breakpoint.bplist.keys():
4017db96d56Sopenharmony_ci            self._add_to_breaks(filename, lineno)
4027db96d56Sopenharmony_ci
4037db96d56Sopenharmony_ci    def _prune_breaks(self, filename, lineno):
4047db96d56Sopenharmony_ci        """Prune breakpoints for filename:lineno.
4057db96d56Sopenharmony_ci
4067db96d56Sopenharmony_ci        A list of breakpoints is maintained in the Bdb instance and in
4077db96d56Sopenharmony_ci        the Breakpoint class.  If a breakpoint in the Bdb instance no
4087db96d56Sopenharmony_ci        longer exists in the Breakpoint class, then it's removed from the
4097db96d56Sopenharmony_ci        Bdb instance.
4107db96d56Sopenharmony_ci        """
4117db96d56Sopenharmony_ci        if (filename, lineno) not in Breakpoint.bplist:
4127db96d56Sopenharmony_ci            self.breaks[filename].remove(lineno)
4137db96d56Sopenharmony_ci        if not self.breaks[filename]:
4147db96d56Sopenharmony_ci            del self.breaks[filename]
4157db96d56Sopenharmony_ci
4167db96d56Sopenharmony_ci    def clear_break(self, filename, lineno):
4177db96d56Sopenharmony_ci        """Delete breakpoints for filename:lineno.
4187db96d56Sopenharmony_ci
4197db96d56Sopenharmony_ci        If no breakpoints were set, return an error message.
4207db96d56Sopenharmony_ci        """
4217db96d56Sopenharmony_ci        filename = self.canonic(filename)
4227db96d56Sopenharmony_ci        if filename not in self.breaks:
4237db96d56Sopenharmony_ci            return 'There are no breakpoints in %s' % filename
4247db96d56Sopenharmony_ci        if lineno not in self.breaks[filename]:
4257db96d56Sopenharmony_ci            return 'There is no breakpoint at %s:%d' % (filename, lineno)
4267db96d56Sopenharmony_ci        # If there's only one bp in the list for that file,line
4277db96d56Sopenharmony_ci        # pair, then remove the breaks entry
4287db96d56Sopenharmony_ci        for bp in Breakpoint.bplist[filename, lineno][:]:
4297db96d56Sopenharmony_ci            bp.deleteMe()
4307db96d56Sopenharmony_ci        self._prune_breaks(filename, lineno)
4317db96d56Sopenharmony_ci        return None
4327db96d56Sopenharmony_ci
4337db96d56Sopenharmony_ci    def clear_bpbynumber(self, arg):
4347db96d56Sopenharmony_ci        """Delete a breakpoint by its index in Breakpoint.bpbynumber.
4357db96d56Sopenharmony_ci
4367db96d56Sopenharmony_ci        If arg is invalid, return an error message.
4377db96d56Sopenharmony_ci        """
4387db96d56Sopenharmony_ci        try:
4397db96d56Sopenharmony_ci            bp = self.get_bpbynumber(arg)
4407db96d56Sopenharmony_ci        except ValueError as err:
4417db96d56Sopenharmony_ci            return str(err)
4427db96d56Sopenharmony_ci        bp.deleteMe()
4437db96d56Sopenharmony_ci        self._prune_breaks(bp.file, bp.line)
4447db96d56Sopenharmony_ci        return None
4457db96d56Sopenharmony_ci
4467db96d56Sopenharmony_ci    def clear_all_file_breaks(self, filename):
4477db96d56Sopenharmony_ci        """Delete all breakpoints in filename.
4487db96d56Sopenharmony_ci
4497db96d56Sopenharmony_ci        If none were set, return an error message.
4507db96d56Sopenharmony_ci        """
4517db96d56Sopenharmony_ci        filename = self.canonic(filename)
4527db96d56Sopenharmony_ci        if filename not in self.breaks:
4537db96d56Sopenharmony_ci            return 'There are no breakpoints in %s' % filename
4547db96d56Sopenharmony_ci        for line in self.breaks[filename]:
4557db96d56Sopenharmony_ci            blist = Breakpoint.bplist[filename, line]
4567db96d56Sopenharmony_ci            for bp in blist:
4577db96d56Sopenharmony_ci                bp.deleteMe()
4587db96d56Sopenharmony_ci        del self.breaks[filename]
4597db96d56Sopenharmony_ci        return None
4607db96d56Sopenharmony_ci
4617db96d56Sopenharmony_ci    def clear_all_breaks(self):
4627db96d56Sopenharmony_ci        """Delete all existing breakpoints.
4637db96d56Sopenharmony_ci
4647db96d56Sopenharmony_ci        If none were set, return an error message.
4657db96d56Sopenharmony_ci        """
4667db96d56Sopenharmony_ci        if not self.breaks:
4677db96d56Sopenharmony_ci            return 'There are no breakpoints'
4687db96d56Sopenharmony_ci        for bp in Breakpoint.bpbynumber:
4697db96d56Sopenharmony_ci            if bp:
4707db96d56Sopenharmony_ci                bp.deleteMe()
4717db96d56Sopenharmony_ci        self.breaks = {}
4727db96d56Sopenharmony_ci        return None
4737db96d56Sopenharmony_ci
4747db96d56Sopenharmony_ci    def get_bpbynumber(self, arg):
4757db96d56Sopenharmony_ci        """Return a breakpoint by its index in Breakpoint.bybpnumber.
4767db96d56Sopenharmony_ci
4777db96d56Sopenharmony_ci        For invalid arg values or if the breakpoint doesn't exist,
4787db96d56Sopenharmony_ci        raise a ValueError.
4797db96d56Sopenharmony_ci        """
4807db96d56Sopenharmony_ci        if not arg:
4817db96d56Sopenharmony_ci            raise ValueError('Breakpoint number expected')
4827db96d56Sopenharmony_ci        try:
4837db96d56Sopenharmony_ci            number = int(arg)
4847db96d56Sopenharmony_ci        except ValueError:
4857db96d56Sopenharmony_ci            raise ValueError('Non-numeric breakpoint number %s' % arg) from None
4867db96d56Sopenharmony_ci        try:
4877db96d56Sopenharmony_ci            bp = Breakpoint.bpbynumber[number]
4887db96d56Sopenharmony_ci        except IndexError:
4897db96d56Sopenharmony_ci            raise ValueError('Breakpoint number %d out of range' % number) from None
4907db96d56Sopenharmony_ci        if bp is None:
4917db96d56Sopenharmony_ci            raise ValueError('Breakpoint %d already deleted' % number)
4927db96d56Sopenharmony_ci        return bp
4937db96d56Sopenharmony_ci
4947db96d56Sopenharmony_ci    def get_break(self, filename, lineno):
4957db96d56Sopenharmony_ci        """Return True if there is a breakpoint for filename:lineno."""
4967db96d56Sopenharmony_ci        filename = self.canonic(filename)
4977db96d56Sopenharmony_ci        return filename in self.breaks and \
4987db96d56Sopenharmony_ci            lineno in self.breaks[filename]
4997db96d56Sopenharmony_ci
5007db96d56Sopenharmony_ci    def get_breaks(self, filename, lineno):
5017db96d56Sopenharmony_ci        """Return all breakpoints for filename:lineno.
5027db96d56Sopenharmony_ci
5037db96d56Sopenharmony_ci        If no breakpoints are set, return an empty list.
5047db96d56Sopenharmony_ci        """
5057db96d56Sopenharmony_ci        filename = self.canonic(filename)
5067db96d56Sopenharmony_ci        return filename in self.breaks and \
5077db96d56Sopenharmony_ci            lineno in self.breaks[filename] and \
5087db96d56Sopenharmony_ci            Breakpoint.bplist[filename, lineno] or []
5097db96d56Sopenharmony_ci
5107db96d56Sopenharmony_ci    def get_file_breaks(self, filename):
5117db96d56Sopenharmony_ci        """Return all lines with breakpoints for filename.
5127db96d56Sopenharmony_ci
5137db96d56Sopenharmony_ci        If no breakpoints are set, return an empty list.
5147db96d56Sopenharmony_ci        """
5157db96d56Sopenharmony_ci        filename = self.canonic(filename)
5167db96d56Sopenharmony_ci        if filename in self.breaks:
5177db96d56Sopenharmony_ci            return self.breaks[filename]
5187db96d56Sopenharmony_ci        else:
5197db96d56Sopenharmony_ci            return []
5207db96d56Sopenharmony_ci
5217db96d56Sopenharmony_ci    def get_all_breaks(self):
5227db96d56Sopenharmony_ci        """Return all breakpoints that are set."""
5237db96d56Sopenharmony_ci        return self.breaks
5247db96d56Sopenharmony_ci
5257db96d56Sopenharmony_ci    # Derived classes and clients can call the following method
5267db96d56Sopenharmony_ci    # to get a data structure representing a stack trace.
5277db96d56Sopenharmony_ci
5287db96d56Sopenharmony_ci    def get_stack(self, f, t):
5297db96d56Sopenharmony_ci        """Return a list of (frame, lineno) in a stack trace and a size.
5307db96d56Sopenharmony_ci
5317db96d56Sopenharmony_ci        List starts with original calling frame, if there is one.
5327db96d56Sopenharmony_ci        Size may be number of frames above or below f.
5337db96d56Sopenharmony_ci        """
5347db96d56Sopenharmony_ci        stack = []
5357db96d56Sopenharmony_ci        if t and t.tb_frame is f:
5367db96d56Sopenharmony_ci            t = t.tb_next
5377db96d56Sopenharmony_ci        while f is not None:
5387db96d56Sopenharmony_ci            stack.append((f, f.f_lineno))
5397db96d56Sopenharmony_ci            if f is self.botframe:
5407db96d56Sopenharmony_ci                break
5417db96d56Sopenharmony_ci            f = f.f_back
5427db96d56Sopenharmony_ci        stack.reverse()
5437db96d56Sopenharmony_ci        i = max(0, len(stack) - 1)
5447db96d56Sopenharmony_ci        while t is not None:
5457db96d56Sopenharmony_ci            stack.append((t.tb_frame, t.tb_lineno))
5467db96d56Sopenharmony_ci            t = t.tb_next
5477db96d56Sopenharmony_ci        if f is None:
5487db96d56Sopenharmony_ci            i = max(0, len(stack) - 1)
5497db96d56Sopenharmony_ci        return stack, i
5507db96d56Sopenharmony_ci
5517db96d56Sopenharmony_ci    def format_stack_entry(self, frame_lineno, lprefix=': '):
5527db96d56Sopenharmony_ci        """Return a string with information about a stack entry.
5537db96d56Sopenharmony_ci
5547db96d56Sopenharmony_ci        The stack entry frame_lineno is a (frame, lineno) tuple.  The
5557db96d56Sopenharmony_ci        return string contains the canonical filename, the function name
5567db96d56Sopenharmony_ci        or '<lambda>', the input arguments, the return value, and the
5577db96d56Sopenharmony_ci        line of code (if it exists).
5587db96d56Sopenharmony_ci
5597db96d56Sopenharmony_ci        """
5607db96d56Sopenharmony_ci        import linecache, reprlib
5617db96d56Sopenharmony_ci        frame, lineno = frame_lineno
5627db96d56Sopenharmony_ci        filename = self.canonic(frame.f_code.co_filename)
5637db96d56Sopenharmony_ci        s = '%s(%r)' % (filename, lineno)
5647db96d56Sopenharmony_ci        if frame.f_code.co_name:
5657db96d56Sopenharmony_ci            s += frame.f_code.co_name
5667db96d56Sopenharmony_ci        else:
5677db96d56Sopenharmony_ci            s += "<lambda>"
5687db96d56Sopenharmony_ci        s += '()'
5697db96d56Sopenharmony_ci        if '__return__' in frame.f_locals:
5707db96d56Sopenharmony_ci            rv = frame.f_locals['__return__']
5717db96d56Sopenharmony_ci            s += '->'
5727db96d56Sopenharmony_ci            s += reprlib.repr(rv)
5737db96d56Sopenharmony_ci        if lineno is not None:
5747db96d56Sopenharmony_ci            line = linecache.getline(filename, lineno, frame.f_globals)
5757db96d56Sopenharmony_ci            if line:
5767db96d56Sopenharmony_ci                s += lprefix + line.strip()
5777db96d56Sopenharmony_ci        else:
5787db96d56Sopenharmony_ci            s += f'{lprefix}Warning: lineno is None'
5797db96d56Sopenharmony_ci        return s
5807db96d56Sopenharmony_ci
5817db96d56Sopenharmony_ci    # The following methods can be called by clients to use
5827db96d56Sopenharmony_ci    # a debugger to debug a statement or an expression.
5837db96d56Sopenharmony_ci    # Both can be given as a string, or a code object.
5847db96d56Sopenharmony_ci
5857db96d56Sopenharmony_ci    def run(self, cmd, globals=None, locals=None):
5867db96d56Sopenharmony_ci        """Debug a statement executed via the exec() function.
5877db96d56Sopenharmony_ci
5887db96d56Sopenharmony_ci        globals defaults to __main__.dict; locals defaults to globals.
5897db96d56Sopenharmony_ci        """
5907db96d56Sopenharmony_ci        if globals is None:
5917db96d56Sopenharmony_ci            import __main__
5927db96d56Sopenharmony_ci            globals = __main__.__dict__
5937db96d56Sopenharmony_ci        if locals is None:
5947db96d56Sopenharmony_ci            locals = globals
5957db96d56Sopenharmony_ci        self.reset()
5967db96d56Sopenharmony_ci        if isinstance(cmd, str):
5977db96d56Sopenharmony_ci            cmd = compile(cmd, "<string>", "exec")
5987db96d56Sopenharmony_ci        sys.settrace(self.trace_dispatch)
5997db96d56Sopenharmony_ci        try:
6007db96d56Sopenharmony_ci            exec(cmd, globals, locals)
6017db96d56Sopenharmony_ci        except BdbQuit:
6027db96d56Sopenharmony_ci            pass
6037db96d56Sopenharmony_ci        finally:
6047db96d56Sopenharmony_ci            self.quitting = True
6057db96d56Sopenharmony_ci            sys.settrace(None)
6067db96d56Sopenharmony_ci
6077db96d56Sopenharmony_ci    def runeval(self, expr, globals=None, locals=None):
6087db96d56Sopenharmony_ci        """Debug an expression executed via the eval() function.
6097db96d56Sopenharmony_ci
6107db96d56Sopenharmony_ci        globals defaults to __main__.dict; locals defaults to globals.
6117db96d56Sopenharmony_ci        """
6127db96d56Sopenharmony_ci        if globals is None:
6137db96d56Sopenharmony_ci            import __main__
6147db96d56Sopenharmony_ci            globals = __main__.__dict__
6157db96d56Sopenharmony_ci        if locals is None:
6167db96d56Sopenharmony_ci            locals = globals
6177db96d56Sopenharmony_ci        self.reset()
6187db96d56Sopenharmony_ci        sys.settrace(self.trace_dispatch)
6197db96d56Sopenharmony_ci        try:
6207db96d56Sopenharmony_ci            return eval(expr, globals, locals)
6217db96d56Sopenharmony_ci        except BdbQuit:
6227db96d56Sopenharmony_ci            pass
6237db96d56Sopenharmony_ci        finally:
6247db96d56Sopenharmony_ci            self.quitting = True
6257db96d56Sopenharmony_ci            sys.settrace(None)
6267db96d56Sopenharmony_ci
6277db96d56Sopenharmony_ci    def runctx(self, cmd, globals, locals):
6287db96d56Sopenharmony_ci        """For backwards-compatibility.  Defers to run()."""
6297db96d56Sopenharmony_ci        # B/W compatibility
6307db96d56Sopenharmony_ci        self.run(cmd, globals, locals)
6317db96d56Sopenharmony_ci
6327db96d56Sopenharmony_ci    # This method is more useful to debug a single function call.
6337db96d56Sopenharmony_ci
6347db96d56Sopenharmony_ci    def runcall(self, func, /, *args, **kwds):
6357db96d56Sopenharmony_ci        """Debug a single function call.
6367db96d56Sopenharmony_ci
6377db96d56Sopenharmony_ci        Return the result of the function call.
6387db96d56Sopenharmony_ci        """
6397db96d56Sopenharmony_ci        self.reset()
6407db96d56Sopenharmony_ci        sys.settrace(self.trace_dispatch)
6417db96d56Sopenharmony_ci        res = None
6427db96d56Sopenharmony_ci        try:
6437db96d56Sopenharmony_ci            res = func(*args, **kwds)
6447db96d56Sopenharmony_ci        except BdbQuit:
6457db96d56Sopenharmony_ci            pass
6467db96d56Sopenharmony_ci        finally:
6477db96d56Sopenharmony_ci            self.quitting = True
6487db96d56Sopenharmony_ci            sys.settrace(None)
6497db96d56Sopenharmony_ci        return res
6507db96d56Sopenharmony_ci
6517db96d56Sopenharmony_ci
6527db96d56Sopenharmony_cidef set_trace():
6537db96d56Sopenharmony_ci    """Start debugging with a Bdb instance from the caller's frame."""
6547db96d56Sopenharmony_ci    Bdb().set_trace()
6557db96d56Sopenharmony_ci
6567db96d56Sopenharmony_ci
6577db96d56Sopenharmony_ciclass Breakpoint:
6587db96d56Sopenharmony_ci    """Breakpoint class.
6597db96d56Sopenharmony_ci
6607db96d56Sopenharmony_ci    Implements temporary breakpoints, ignore counts, disabling and
6617db96d56Sopenharmony_ci    (re)-enabling, and conditionals.
6627db96d56Sopenharmony_ci
6637db96d56Sopenharmony_ci    Breakpoints are indexed by number through bpbynumber and by
6647db96d56Sopenharmony_ci    the (file, line) tuple using bplist.  The former points to a
6657db96d56Sopenharmony_ci    single instance of class Breakpoint.  The latter points to a
6667db96d56Sopenharmony_ci    list of such instances since there may be more than one
6677db96d56Sopenharmony_ci    breakpoint per line.
6687db96d56Sopenharmony_ci
6697db96d56Sopenharmony_ci    When creating a breakpoint, its associated filename should be
6707db96d56Sopenharmony_ci    in canonical form.  If funcname is defined, a breakpoint hit will be
6717db96d56Sopenharmony_ci    counted when the first line of that function is executed.  A
6727db96d56Sopenharmony_ci    conditional breakpoint always counts a hit.
6737db96d56Sopenharmony_ci    """
6747db96d56Sopenharmony_ci
6757db96d56Sopenharmony_ci    # XXX Keeping state in the class is a mistake -- this means
6767db96d56Sopenharmony_ci    # you cannot have more than one active Bdb instance.
6777db96d56Sopenharmony_ci
6787db96d56Sopenharmony_ci    next = 1        # Next bp to be assigned
6797db96d56Sopenharmony_ci    bplist = {}     # indexed by (file, lineno) tuple
6807db96d56Sopenharmony_ci    bpbynumber = [None] # Each entry is None or an instance of Bpt
6817db96d56Sopenharmony_ci                # index 0 is unused, except for marking an
6827db96d56Sopenharmony_ci                # effective break .... see effective()
6837db96d56Sopenharmony_ci
6847db96d56Sopenharmony_ci    def __init__(self, file, line, temporary=False, cond=None, funcname=None):
6857db96d56Sopenharmony_ci        self.funcname = funcname
6867db96d56Sopenharmony_ci        # Needed if funcname is not None.
6877db96d56Sopenharmony_ci        self.func_first_executable_line = None
6887db96d56Sopenharmony_ci        self.file = file    # This better be in canonical form!
6897db96d56Sopenharmony_ci        self.line = line
6907db96d56Sopenharmony_ci        self.temporary = temporary
6917db96d56Sopenharmony_ci        self.cond = cond
6927db96d56Sopenharmony_ci        self.enabled = True
6937db96d56Sopenharmony_ci        self.ignore = 0
6947db96d56Sopenharmony_ci        self.hits = 0
6957db96d56Sopenharmony_ci        self.number = Breakpoint.next
6967db96d56Sopenharmony_ci        Breakpoint.next += 1
6977db96d56Sopenharmony_ci        # Build the two lists
6987db96d56Sopenharmony_ci        self.bpbynumber.append(self)
6997db96d56Sopenharmony_ci        if (file, line) in self.bplist:
7007db96d56Sopenharmony_ci            self.bplist[file, line].append(self)
7017db96d56Sopenharmony_ci        else:
7027db96d56Sopenharmony_ci            self.bplist[file, line] = [self]
7037db96d56Sopenharmony_ci
7047db96d56Sopenharmony_ci    @staticmethod
7057db96d56Sopenharmony_ci    def clearBreakpoints():
7067db96d56Sopenharmony_ci        Breakpoint.next = 1
7077db96d56Sopenharmony_ci        Breakpoint.bplist = {}
7087db96d56Sopenharmony_ci        Breakpoint.bpbynumber = [None]
7097db96d56Sopenharmony_ci
7107db96d56Sopenharmony_ci    def deleteMe(self):
7117db96d56Sopenharmony_ci        """Delete the breakpoint from the list associated to a file:line.
7127db96d56Sopenharmony_ci
7137db96d56Sopenharmony_ci        If it is the last breakpoint in that position, it also deletes
7147db96d56Sopenharmony_ci        the entry for the file:line.
7157db96d56Sopenharmony_ci        """
7167db96d56Sopenharmony_ci
7177db96d56Sopenharmony_ci        index = (self.file, self.line)
7187db96d56Sopenharmony_ci        self.bpbynumber[self.number] = None   # No longer in list
7197db96d56Sopenharmony_ci        self.bplist[index].remove(self)
7207db96d56Sopenharmony_ci        if not self.bplist[index]:
7217db96d56Sopenharmony_ci            # No more bp for this f:l combo
7227db96d56Sopenharmony_ci            del self.bplist[index]
7237db96d56Sopenharmony_ci
7247db96d56Sopenharmony_ci    def enable(self):
7257db96d56Sopenharmony_ci        """Mark the breakpoint as enabled."""
7267db96d56Sopenharmony_ci        self.enabled = True
7277db96d56Sopenharmony_ci
7287db96d56Sopenharmony_ci    def disable(self):
7297db96d56Sopenharmony_ci        """Mark the breakpoint as disabled."""
7307db96d56Sopenharmony_ci        self.enabled = False
7317db96d56Sopenharmony_ci
7327db96d56Sopenharmony_ci    def bpprint(self, out=None):
7337db96d56Sopenharmony_ci        """Print the output of bpformat().
7347db96d56Sopenharmony_ci
7357db96d56Sopenharmony_ci        The optional out argument directs where the output is sent
7367db96d56Sopenharmony_ci        and defaults to standard output.
7377db96d56Sopenharmony_ci        """
7387db96d56Sopenharmony_ci        if out is None:
7397db96d56Sopenharmony_ci            out = sys.stdout
7407db96d56Sopenharmony_ci        print(self.bpformat(), file=out)
7417db96d56Sopenharmony_ci
7427db96d56Sopenharmony_ci    def bpformat(self):
7437db96d56Sopenharmony_ci        """Return a string with information about the breakpoint.
7447db96d56Sopenharmony_ci
7457db96d56Sopenharmony_ci        The information includes the breakpoint number, temporary
7467db96d56Sopenharmony_ci        status, file:line position, break condition, number of times to
7477db96d56Sopenharmony_ci        ignore, and number of times hit.
7487db96d56Sopenharmony_ci
7497db96d56Sopenharmony_ci        """
7507db96d56Sopenharmony_ci        if self.temporary:
7517db96d56Sopenharmony_ci            disp = 'del  '
7527db96d56Sopenharmony_ci        else:
7537db96d56Sopenharmony_ci            disp = 'keep '
7547db96d56Sopenharmony_ci        if self.enabled:
7557db96d56Sopenharmony_ci            disp = disp + 'yes  '
7567db96d56Sopenharmony_ci        else:
7577db96d56Sopenharmony_ci            disp = disp + 'no   '
7587db96d56Sopenharmony_ci        ret = '%-4dbreakpoint   %s at %s:%d' % (self.number, disp,
7597db96d56Sopenharmony_ci                                                self.file, self.line)
7607db96d56Sopenharmony_ci        if self.cond:
7617db96d56Sopenharmony_ci            ret += '\n\tstop only if %s' % (self.cond,)
7627db96d56Sopenharmony_ci        if self.ignore:
7637db96d56Sopenharmony_ci            ret += '\n\tignore next %d hits' % (self.ignore,)
7647db96d56Sopenharmony_ci        if self.hits:
7657db96d56Sopenharmony_ci            if self.hits > 1:
7667db96d56Sopenharmony_ci                ss = 's'
7677db96d56Sopenharmony_ci            else:
7687db96d56Sopenharmony_ci                ss = ''
7697db96d56Sopenharmony_ci            ret += '\n\tbreakpoint already hit %d time%s' % (self.hits, ss)
7707db96d56Sopenharmony_ci        return ret
7717db96d56Sopenharmony_ci
7727db96d56Sopenharmony_ci    def __str__(self):
7737db96d56Sopenharmony_ci        "Return a condensed description of the breakpoint."
7747db96d56Sopenharmony_ci        return 'breakpoint %s at %s:%s' % (self.number, self.file, self.line)
7757db96d56Sopenharmony_ci
7767db96d56Sopenharmony_ci# -----------end of Breakpoint class----------
7777db96d56Sopenharmony_ci
7787db96d56Sopenharmony_ci
7797db96d56Sopenharmony_cidef checkfuncname(b, frame):
7807db96d56Sopenharmony_ci    """Return True if break should happen here.
7817db96d56Sopenharmony_ci
7827db96d56Sopenharmony_ci    Whether a break should happen depends on the way that b (the breakpoint)
7837db96d56Sopenharmony_ci    was set.  If it was set via line number, check if b.line is the same as
7847db96d56Sopenharmony_ci    the one in the frame.  If it was set via function name, check if this is
7857db96d56Sopenharmony_ci    the right function and if it is on the first executable line.
7867db96d56Sopenharmony_ci    """
7877db96d56Sopenharmony_ci    if not b.funcname:
7887db96d56Sopenharmony_ci        # Breakpoint was set via line number.
7897db96d56Sopenharmony_ci        if b.line != frame.f_lineno:
7907db96d56Sopenharmony_ci            # Breakpoint was set at a line with a def statement and the function
7917db96d56Sopenharmony_ci            # defined is called: don't break.
7927db96d56Sopenharmony_ci            return False
7937db96d56Sopenharmony_ci        return True
7947db96d56Sopenharmony_ci
7957db96d56Sopenharmony_ci    # Breakpoint set via function name.
7967db96d56Sopenharmony_ci    if frame.f_code.co_name != b.funcname:
7977db96d56Sopenharmony_ci        # It's not a function call, but rather execution of def statement.
7987db96d56Sopenharmony_ci        return False
7997db96d56Sopenharmony_ci
8007db96d56Sopenharmony_ci    # We are in the right frame.
8017db96d56Sopenharmony_ci    if not b.func_first_executable_line:
8027db96d56Sopenharmony_ci        # The function is entered for the 1st time.
8037db96d56Sopenharmony_ci        b.func_first_executable_line = frame.f_lineno
8047db96d56Sopenharmony_ci
8057db96d56Sopenharmony_ci    if b.func_first_executable_line != frame.f_lineno:
8067db96d56Sopenharmony_ci        # But we are not at the first line number: don't break.
8077db96d56Sopenharmony_ci        return False
8087db96d56Sopenharmony_ci    return True
8097db96d56Sopenharmony_ci
8107db96d56Sopenharmony_ci
8117db96d56Sopenharmony_cidef effective(file, line, frame):
8127db96d56Sopenharmony_ci    """Return (active breakpoint, delete temporary flag) or (None, None) as
8137db96d56Sopenharmony_ci       breakpoint to act upon.
8147db96d56Sopenharmony_ci
8157db96d56Sopenharmony_ci       The "active breakpoint" is the first entry in bplist[line, file] (which
8167db96d56Sopenharmony_ci       must exist) that is enabled, for which checkfuncname is True, and that
8177db96d56Sopenharmony_ci       has neither a False condition nor a positive ignore count.  The flag,
8187db96d56Sopenharmony_ci       meaning that a temporary breakpoint should be deleted, is False only
8197db96d56Sopenharmony_ci       when the condiion cannot be evaluated (in which case, ignore count is
8207db96d56Sopenharmony_ci       ignored).
8217db96d56Sopenharmony_ci
8227db96d56Sopenharmony_ci       If no such entry exists, then (None, None) is returned.
8237db96d56Sopenharmony_ci    """
8247db96d56Sopenharmony_ci    possibles = Breakpoint.bplist[file, line]
8257db96d56Sopenharmony_ci    for b in possibles:
8267db96d56Sopenharmony_ci        if not b.enabled:
8277db96d56Sopenharmony_ci            continue
8287db96d56Sopenharmony_ci        if not checkfuncname(b, frame):
8297db96d56Sopenharmony_ci            continue
8307db96d56Sopenharmony_ci        # Count every hit when bp is enabled
8317db96d56Sopenharmony_ci        b.hits += 1
8327db96d56Sopenharmony_ci        if not b.cond:
8337db96d56Sopenharmony_ci            # If unconditional, and ignoring go on to next, else break
8347db96d56Sopenharmony_ci            if b.ignore > 0:
8357db96d56Sopenharmony_ci                b.ignore -= 1
8367db96d56Sopenharmony_ci                continue
8377db96d56Sopenharmony_ci            else:
8387db96d56Sopenharmony_ci                # breakpoint and marker that it's ok to delete if temporary
8397db96d56Sopenharmony_ci                return (b, True)
8407db96d56Sopenharmony_ci        else:
8417db96d56Sopenharmony_ci            # Conditional bp.
8427db96d56Sopenharmony_ci            # Ignore count applies only to those bpt hits where the
8437db96d56Sopenharmony_ci            # condition evaluates to true.
8447db96d56Sopenharmony_ci            try:
8457db96d56Sopenharmony_ci                val = eval(b.cond, frame.f_globals, frame.f_locals)
8467db96d56Sopenharmony_ci                if val:
8477db96d56Sopenharmony_ci                    if b.ignore > 0:
8487db96d56Sopenharmony_ci                        b.ignore -= 1
8497db96d56Sopenharmony_ci                        # continue
8507db96d56Sopenharmony_ci                    else:
8517db96d56Sopenharmony_ci                        return (b, True)
8527db96d56Sopenharmony_ci                # else:
8537db96d56Sopenharmony_ci                #   continue
8547db96d56Sopenharmony_ci            except:
8557db96d56Sopenharmony_ci                # if eval fails, most conservative thing is to stop on
8567db96d56Sopenharmony_ci                # breakpoint regardless of ignore count.  Don't delete
8577db96d56Sopenharmony_ci                # temporary, as another hint to user.
8587db96d56Sopenharmony_ci                return (b, False)
8597db96d56Sopenharmony_ci    return (None, None)
8607db96d56Sopenharmony_ci
8617db96d56Sopenharmony_ci
8627db96d56Sopenharmony_ci# -------------------- testing --------------------
8637db96d56Sopenharmony_ci
8647db96d56Sopenharmony_ciclass Tdb(Bdb):
8657db96d56Sopenharmony_ci    def user_call(self, frame, args):
8667db96d56Sopenharmony_ci        name = frame.f_code.co_name
8677db96d56Sopenharmony_ci        if not name: name = '???'
8687db96d56Sopenharmony_ci        print('+++ call', name, args)
8697db96d56Sopenharmony_ci    def user_line(self, frame):
8707db96d56Sopenharmony_ci        import linecache
8717db96d56Sopenharmony_ci        name = frame.f_code.co_name
8727db96d56Sopenharmony_ci        if not name: name = '???'
8737db96d56Sopenharmony_ci        fn = self.canonic(frame.f_code.co_filename)
8747db96d56Sopenharmony_ci        line = linecache.getline(fn, frame.f_lineno, frame.f_globals)
8757db96d56Sopenharmony_ci        print('+++', fn, frame.f_lineno, name, ':', line.strip())
8767db96d56Sopenharmony_ci    def user_return(self, frame, retval):
8777db96d56Sopenharmony_ci        print('+++ return', retval)
8787db96d56Sopenharmony_ci    def user_exception(self, frame, exc_stuff):
8797db96d56Sopenharmony_ci        print('+++ exception', exc_stuff)
8807db96d56Sopenharmony_ci        self.set_continue()
8817db96d56Sopenharmony_ci
8827db96d56Sopenharmony_cidef foo(n):
8837db96d56Sopenharmony_ci    print('foo(', n, ')')
8847db96d56Sopenharmony_ci    x = bar(n*10)
8857db96d56Sopenharmony_ci    print('bar returned', x)
8867db96d56Sopenharmony_ci
8877db96d56Sopenharmony_cidef bar(a):
8887db96d56Sopenharmony_ci    print('bar(', a, ')')
8897db96d56Sopenharmony_ci    return a/2
8907db96d56Sopenharmony_ci
8917db96d56Sopenharmony_cidef test():
8927db96d56Sopenharmony_ci    t = Tdb()
8937db96d56Sopenharmony_ci    t.run('import bdb; bdb.foo(10)')
894