Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
bdb.py1207 linesDownload Raw Back to Lib
1"""Debugger basics"""2 3import fnmatch4import sys5import threading6import os7import weakref8from contextlib import contextmanager9from inspect import CO_GENERATOR, CO_COROUTINE, CO_ASYNC_GENERATOR10 11__all__ = ["BdbQuit", "Bdb", "Breakpoint"]12 13GENERATOR_AND_COROUTINE_FLAGS = CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR14 15 16class BdbQuit(Exception):17    """Exception to give up completely."""18 19 20E = sys.monitoring.events21 22class _MonitoringTracer:23    EVENT_CALLBACK_MAP = {24        E.PY_START: 'call',25        E.PY_RESUME: 'call',26        E.PY_THROW: 'call',27        E.LINE: 'line',28        E.JUMP: 'jump',29        E.PY_RETURN: 'return',30        E.PY_YIELD: 'return',31        E.PY_UNWIND: 'unwind',32        E.RAISE: 'exception',33        E.STOP_ITERATION: 'exception',34        E.INSTRUCTION: 'opcode',35    }36 37    GLOBAL_EVENTS = E.PY_START | E.PY_RESUME | E.PY_THROW | E.PY_UNWIND | E.RAISE38    LOCAL_EVENTS = E.LINE | E.JUMP | E.PY_RETURN | E.PY_YIELD | E.STOP_ITERATION39 40    def __init__(self):41        self._tool_id = sys.monitoring.DEBUGGER_ID42        self._name = 'bdbtracer'43        self._tracefunc = None44        self._disable_current_event = False45        self._tracing_thread = None46        self._enabled = False47 48    def start_trace(self, tracefunc):49        self._tracefunc = tracefunc50        self._tracing_thread = threading.current_thread()51        curr_tool = sys.monitoring.get_tool(self._tool_id)52        if curr_tool is None:53            sys.monitoring.use_tool_id(self._tool_id, self._name)54        elif curr_tool == self._name:55            sys.monitoring.clear_tool_id(self._tool_id)56        else:57            raise ValueError('Another debugger is using the monitoring tool')58        E = sys.monitoring.events59        all_events = 060        for event, cb_name in self.EVENT_CALLBACK_MAP.items():61            callback = self.callback_wrapper(getattr(self, f'{cb_name}_callback'), event)62            sys.monitoring.register_callback(self._tool_id, event, callback)63            if event != E.INSTRUCTION:64                all_events |= event65        self.update_local_events()66        sys.monitoring.set_events(self._tool_id, self.GLOBAL_EVENTS)67        self._enabled = True68 69    def stop_trace(self):70        self._enabled = False71        self._tracing_thread = None72        curr_tool = sys.monitoring.get_tool(self._tool_id)73        if curr_tool != self._name:74            return75        sys.monitoring.clear_tool_id(self._tool_id)76        sys.monitoring.free_tool_id(self._tool_id)77 78    def disable_current_event(self):79        self._disable_current_event = True80 81    def restart_events(self):82        if sys.monitoring.get_tool(self._tool_id) == self._name:83            sys.monitoring.restart_events()84 85    def callback_wrapper(self, func, event):86        import functools87 88        @functools.wraps(func)89        def wrapper(*args):90            if self._tracing_thread != threading.current_thread():91                return92            try:93                frame = sys._getframe().f_back94                ret = func(frame, *args)95                if self._enabled and frame.f_trace:96                    self.update_local_events()97                if (98                    self._disable_current_event99                    and event not in (E.PY_THROW, E.PY_UNWIND, E.RAISE)100                ):101                    return sys.monitoring.DISABLE102                else:103                    return ret104            except BaseException:105                self.stop_trace()106                sys._getframe().f_back.f_trace = None107                raise108            finally:109                self._disable_current_event = False110 111        return wrapper112 113    def call_callback(self, frame, code, *args):114        local_tracefunc = self._tracefunc(frame, 'call', None)115        if local_tracefunc is not None:116            frame.f_trace = local_tracefunc117            if self._enabled:118                sys.monitoring.set_local_events(self._tool_id, code, self.LOCAL_EVENTS)119 120    def return_callback(self, frame, code, offset, retval):121        if frame.f_trace:122            frame.f_trace(frame, 'return', retval)123 124    def unwind_callback(self, frame, code, *args):125        if frame.f_trace:126            frame.f_trace(frame, 'return', None)127 128    def line_callback(self, frame, code, *args):129        if frame.f_trace and frame.f_trace_lines:130            frame.f_trace(frame, 'line', None)131 132    def jump_callback(self, frame, code, inst_offset, dest_offset):133        if dest_offset > inst_offset:134            return sys.monitoring.DISABLE135        inst_lineno = self._get_lineno(code, inst_offset)136        dest_lineno = self._get_lineno(code, dest_offset)137        if inst_lineno != dest_lineno:138            return sys.monitoring.DISABLE139        if frame.f_trace and frame.f_trace_lines:140            frame.f_trace(frame, 'line', None)141 142    def exception_callback(self, frame, code, offset, exc):143        if frame.f_trace:144            if exc.__traceback__ and hasattr(exc.__traceback__, 'tb_frame'):145                tb = exc.__traceback__146                while tb:147                    if tb.tb_frame.f_locals.get('self') is self:148                        return149                    tb = tb.tb_next150            frame.f_trace(frame, 'exception', (type(exc), exc, exc.__traceback__))151 152    def opcode_callback(self, frame, code, offset):153        if frame.f_trace and frame.f_trace_opcodes:154            frame.f_trace(frame, 'opcode', None)155 156    def update_local_events(self, frame=None):157        if sys.monitoring.get_tool(self._tool_id) != self._name:158            return159        if frame is None:160            frame = sys._getframe().f_back161        while frame is not None:162            if frame.f_trace is not None:163                if frame.f_trace_opcodes:164                    events = self.LOCAL_EVENTS | E.INSTRUCTION165                else:166                    events = self.LOCAL_EVENTS167                sys.monitoring.set_local_events(self._tool_id, frame.f_code, events)168            frame = frame.f_back169 170    def _get_lineno(self, code, offset):171        import dis172        last_lineno = None173        for start, lineno in dis.findlinestarts(code):174            if offset < start:175                return last_lineno176            last_lineno = lineno177        return last_lineno178 179 180class Bdb:181    """Generic Python debugger base class.182 183    This class takes care of details of the trace facility;184    a derived class should implement user interaction.185    The standard debugger class (pdb.Pdb) is an example.186 187    The optional skip argument must be an iterable of glob-style188    module name patterns.  The debugger will not step into frames189    that originate in a module that matches one of these patterns.190    Whether a frame is considered to originate in a certain module191    is determined by the __name__ in the frame globals.192    """193 194    def __init__(self, skip=None, backend='settrace'):195        self.skip = set(skip) if skip else None196        self.breaks = {}197        self.fncache = {}198        self.frame_trace_lines_opcodes = {}199        self.frame_returning = None200        self.trace_opcodes = False201        self.enterframe = None202        self.cmdframe = None203        self.cmdlineno = None204        self.code_linenos = weakref.WeakKeyDictionary()205        self.backend = backend206        if backend == 'monitoring':207            self.monitoring_tracer = _MonitoringTracer()208        elif backend == 'settrace':209            self.monitoring_tracer = None210        else:211            raise ValueError(f"Invalid backend '{backend}'")212 213        self._load_breaks()214 215    def canonic(self, filename):216        """Return canonical form of filename.217 218        For real filenames, the canonical form is a case-normalized (on219        case insensitive filesystems) absolute path.  'Filenames' with220        angle brackets, such as "<stdin>", generated in interactive221        mode, are returned unchanged.222        """223        if filename == "<" + filename[1:-1] + ">":224            return filename225        canonic = self.fncache.get(filename)226        if not canonic:227            canonic = os.path.abspath(filename)228            canonic = os.path.normcase(canonic)229            self.fncache[filename] = canonic230        return canonic231 232    def start_trace(self):233        if self.monitoring_tracer:234            self.monitoring_tracer.start_trace(self.trace_dispatch)235        else:236            sys.settrace(self.trace_dispatch)237 238    def stop_trace(self):239        if self.monitoring_tracer:240            self.monitoring_tracer.stop_trace()241        else:242            sys.settrace(None)243 244    def reset(self):245        """Set values of attributes as ready to start debugging."""246        import linecache247        linecache.checkcache()248        self.botframe = None249        self._set_stopinfo(None, None)250 251    @contextmanager252    def set_enterframe(self, frame):253        self.enterframe = frame254        yield255        self.enterframe = None256 257    def trace_dispatch(self, frame, event, arg):258        """Dispatch a trace function for debugged frames based on the event.259 260        This function is installed as the trace function for debugged261        frames. Its return value is the new trace function, which is262        usually itself. The default implementation decides how to263        dispatch a frame, depending on the type of event (passed in as a264        string) that is about to be executed.265 266        The event can be one of the following:267            line: A new line of code is going to be executed.268            call: A function is about to be called or another code block269                  is entered.270            return: A function or other code block is about to return.271            exception: An exception has occurred.272            c_call: A C function is about to be called.273            c_return: A C function has returned.274            c_exception: A C function has raised an exception.275 276        For the Python events, specialized functions (see the dispatch_*()277        methods) are called.  For the C events, no action is taken.278 279        The arg parameter depends on the previous event.280        """281 282        with self.set_enterframe(frame):283            if self.quitting:284                return # None285            if event == 'line':286                return self.dispatch_line(frame)287            if event == 'call':288                return self.dispatch_call(frame, arg)289            if event == 'return':290                return self.dispatch_return(frame, arg)291            if event == 'exception':292                return self.dispatch_exception(frame, arg)293            if event == 'c_call':294                return self.trace_dispatch295            if event == 'c_exception':296                return self.trace_dispatch297            if event == 'c_return':298                return self.trace_dispatch299            if event == 'opcode':300                return self.dispatch_opcode(frame, arg)301            print('bdb.Bdb.dispatch: unknown debugging event:', repr(event))302            return self.trace_dispatch303 304    def dispatch_line(self, frame):305        """Invoke user function and return trace function for line event.306 307        If the debugger stops on the current line, invoke308        self.user_line(). Raise BdbQuit if self.quitting is set.309        Return self.trace_dispatch to continue tracing in this scope.310        """311        # GH-136057312        # For line events, we don't want to stop at the same line where313        # the latest next/step command was issued.314        if (self.stop_here(frame) or self.break_here(frame)) and not (315            self.cmdframe == frame and self.cmdlineno == frame.f_lineno316        ):317            self.user_line(frame)318            self.restart_events()319            if self.quitting: raise BdbQuit320        elif not self.get_break(frame.f_code.co_filename, frame.f_lineno):321            self.disable_current_event()322        return self.trace_dispatch323 324    def dispatch_call(self, frame, arg):325        """Invoke user function and return trace function for call event.326 327        If the debugger stops on this function call, invoke328        self.user_call(). Raise BdbQuit if self.quitting is set.329        Return self.trace_dispatch to continue tracing in this scope.330        """331        # XXX 'arg' is no longer used332        if self.botframe is None:333            # First call of dispatch since reset()334            self.botframe = frame.f_back # (CT) Note that this may also be None!335            return self.trace_dispatch336        if not (self.stop_here(frame) or self.break_anywhere(frame)):337            # We already know there's no breakpoint in this function338            # If it's a next/until/return command, we don't need any CALL event339            # and we don't need to set the f_trace on any new frame.340            # If it's a step command, it must either hit stop_here, or skip the341            # whole module. Either way, we don't need the CALL event here.342            self.disable_current_event()343            return # None344        # Ignore call events in generator except when stepping.345        if self.stopframe and frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS:346            return self.trace_dispatch347        self.user_call(frame, arg)348        self.restart_events()349        if self.quitting: raise BdbQuit350        return self.trace_dispatch351 352    def dispatch_return(self, frame, arg):353        """Invoke user function and return trace function for return event.354 355        If the debugger stops on this function return, invoke356        self.user_return(). Raise BdbQuit if self.quitting is set.357        Return self.trace_dispatch to continue tracing in this scope.358        """359        if self.stop_here(frame) or frame == self.returnframe:360            # Ignore return events in generator except when stepping.361            if self.stopframe and frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS:362                # It's possible to trigger a StopIteration exception in363                # the caller so we must set the trace function in the caller364                self._set_caller_tracefunc(frame)365                return self.trace_dispatch366            try:367                self.frame_returning = frame368                self.user_return(frame, arg)369                self.restart_events()370            finally:371                self.frame_returning = None372            if self.quitting: raise BdbQuit373            # The user issued a 'next' or 'until' command.374            if self.stopframe is frame and self.stoplineno != -1:375                self._set_stopinfo(None, None)376            # The previous frame might not have f_trace set, unless we are377            # issuing a command that does not expect to stop, we should set378            # f_trace379            if self.stoplineno != -1:380                self._set_caller_tracefunc(frame)381        return self.trace_dispatch382 383    def dispatch_exception(self, frame, arg):384        """Invoke user function and return trace function for exception event.385 386        If the debugger stops on this exception, invoke387        self.user_exception(). Raise BdbQuit if self.quitting is set.388        Return self.trace_dispatch to continue tracing in this scope.389        """390        if self.stop_here(frame):391            # When stepping with next/until/return in a generator frame, skip392            # the internal StopIteration exception (with no traceback)393            # triggered by a subiterator run with the 'yield from' statement.394            if not (frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS395                    and arg[0] is StopIteration and arg[2] is None):396                self.user_exception(frame, arg)397                self.restart_events()398                if self.quitting: raise BdbQuit399        # Stop at the StopIteration or GeneratorExit exception when the user400        # has set stopframe in a generator by issuing a return command, or a401        # next/until command at the last statement in the generator before the402        # exception.403        elif (self.stopframe and frame is not self.stopframe404                and self.stopframe.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS405                and arg[0] in (StopIteration, GeneratorExit)):406            self.user_exception(frame, arg)407            self.restart_events()408            if self.quitting: raise BdbQuit409 410        return self.trace_dispatch411 412    def dispatch_opcode(self, frame, arg):413        """Invoke user function and return trace function for opcode event.414        If the debugger stops on the current opcode, invoke415        self.user_opcode(). Raise BdbQuit if self.quitting is set.416        Return self.trace_dispatch to continue tracing in this scope.417 418        Opcode event will always trigger the user callback. For now the only419        opcode event is from an inline set_trace() and we want to stop there420        unconditionally.421        """422        self.user_opcode(frame)423        self.restart_events()424        if self.quitting: raise BdbQuit425        return self.trace_dispatch426 427    # Normally derived classes don't override the following428    # methods, but they may if they want to redefine the429    # definition of stopping and breakpoints.430 431    def is_skipped_module(self, module_name):432        "Return True if module_name matches any skip pattern."433        if module_name is None:  # some modules do not have names434            return False435        for pattern in self.skip:436            if fnmatch.fnmatch(module_name, pattern):437                return True438        return False439 440    def stop_here(self, frame):441        "Return True if frame is below the starting frame in the stack."442        # (CT) stopframe may now also be None, see dispatch_call.443        # (CT) the former test for None is therefore removed from here.444        if self.skip and \445               self.is_skipped_module(frame.f_globals.get('__name__')):446            return False447        if frame is self.stopframe:448            if self.stoplineno == -1:449                return False450            return frame.f_lineno >= self.stoplineno451        if not self.stopframe:452            return True453        return False454 455    def break_here(self, frame):456        """Return True if there is an effective breakpoint for this line.457 458        Check for line or function breakpoint and if in effect.459        Delete temporary breakpoints if effective() says to.460        """461        filename = self.canonic(frame.f_code.co_filename)462        if filename not in self.breaks:463            return False464        lineno = frame.f_lineno465        if lineno not in self.breaks[filename]:466            # The line itself has no breakpoint, but maybe the line is the467            # first line of a function with breakpoint set by function name.468            lineno = frame.f_code.co_firstlineno469            if lineno not in self.breaks[filename]:470                return False471 472        # flag says ok to delete temp. bp473        (bp, flag) = effective(filename, lineno, frame)474        if bp:475            self.currentbp = bp.number476            if (flag and bp.temporary):477                self.do_clear(str(bp.number))478            return True479        else:480            return False481 482    def do_clear(self, arg):483        """Remove temporary breakpoint.484 485        Must implement in derived classes or get NotImplementedError.486        """487        raise NotImplementedError("subclass of bdb must implement do_clear()")488 489    def break_anywhere(self, frame):490        """Return True if there is any breakpoint in that frame491        """492        filename = self.canonic(frame.f_code.co_filename)493        if filename not in self.breaks:494            return False495        for lineno in self.breaks[filename]:496            if self._lineno_in_frame(lineno, frame):497                return True498        return False499 500    def _lineno_in_frame(self, lineno, frame):501        """Return True if the line number is in the frame's code object.502        """503        code = frame.f_code504        if lineno < code.co_firstlineno:505            return False506        if code not in self.code_linenos:507            self.code_linenos[code] = set(lineno for _, _, lineno in code.co_lines())508        return lineno in self.code_linenos[code]509 510    # Derived classes should override the user_* methods511    # to gain control.512 513    def user_call(self, frame, argument_list):514        """Called if we might stop in a function."""515        pass516 517    def user_line(self, frame):518        """Called when we stop or break at a line."""519        pass520 521    def user_return(self, frame, return_value):522        """Called when a return trap is set here."""523        pass524 525    def user_exception(self, frame, exc_info):526        """Called when we stop on an exception."""527        pass528 529    def user_opcode(self, frame):530        """Called when we are about to execute an opcode."""531        pass532 533    def _set_trace_opcodes(self, trace_opcodes):534        if trace_opcodes != self.trace_opcodes:535            self.trace_opcodes = trace_opcodes536            frame = self.enterframe537            while frame is not None:538                frame.f_trace_opcodes = trace_opcodes539                if frame is self.botframe:540                    break541                frame = frame.f_back542            if self.monitoring_tracer:543                self.monitoring_tracer.update_local_events()544 545    def _set_stopinfo(self, stopframe, returnframe, stoplineno=0, opcode=False,546                      cmdframe=None, cmdlineno=None):547        """Set the attributes for stopping.548 549        If stoplineno is greater than or equal to 0, then stop at line550        greater than or equal to the stopline.  If stoplineno is -1, then551        don't stop at all.552        """553        self.stopframe = stopframe554        self.returnframe = returnframe555        self.quitting = False556        # stoplineno >= 0 means: stop at line >= the stoplineno557        # stoplineno -1 means: don't stop at all558        self.stoplineno = stoplineno559        # cmdframe/cmdlineno is the frame/line number when the user issued560        # step/next commands.561        self.cmdframe = cmdframe562        self.cmdlineno = cmdlineno563        self._set_trace_opcodes(opcode)564 565    def _set_caller_tracefunc(self, current_frame):566        # Issue #13183: pdb skips frames after hitting a breakpoint and running567        # step commands.568        # Restore the trace function in the caller (that may not have been set569        # for performance reasons) when returning from the current frame, unless570        # the caller is the botframe.571        caller_frame = current_frame.f_back572        if caller_frame and not caller_frame.f_trace and caller_frame is not self.botframe:573            caller_frame.f_trace = self.trace_dispatch574 575    # Derived classes and clients can call the following methods576    # to affect the stepping state.577 578    def set_until(self, frame, lineno=None):579        """Stop when the line with the lineno greater than the current one is580        reached or when returning from current frame."""581        # the name "until" is borrowed from gdb582        if lineno is None:583            lineno = frame.f_lineno + 1584        self._set_stopinfo(frame, frame, lineno)585 586    def set_step(self):587        """Stop after one line of code."""588        # set_step() could be called from signal handler so enterframe might be None589        self._set_stopinfo(None, None, cmdframe=self.enterframe,590                           cmdlineno=getattr(self.enterframe, 'f_lineno', None))591 592    def set_stepinstr(self):593        """Stop before the next instruction."""594        self._set_stopinfo(None, None, opcode=True)595 596    def set_next(self, frame):597        """Stop on the next line in or below the given frame."""598        self._set_stopinfo(frame, None, cmdframe=frame, cmdlineno=frame.f_lineno)599 600    def set_return(self, frame):601        """Stop when returning from the given frame."""602        if frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS:603            self._set_stopinfo(frame, frame, -1)604        else:605            self._set_stopinfo(frame.f_back, frame)606 607    def set_trace(self, frame=None):608        """Start debugging from frame.609 610        If frame is not specified, debugging starts from caller's frame.611        """612        self.stop_trace()613        if frame is None:614            frame = sys._getframe().f_back615        self.reset()616        with self.set_enterframe(frame):617            while frame:618                frame.f_trace = self.trace_dispatch619                self.botframe = frame620                self.frame_trace_lines_opcodes[frame] = (frame.f_trace_lines, frame.f_trace_opcodes)621                # We need f_trace_lines == True for the debugger to work622                frame.f_trace_lines = True623                frame = frame.f_back624            self.set_stepinstr()625            self.enterframe = None626        self.start_trace()627 628    def set_continue(self):629        """Stop only at breakpoints or when finished.630 631        If there are no breakpoints, set the system trace function to None.632        """633        # Don't stop except at breakpoints or when finished634        self._set_stopinfo(self.botframe, None, -1)635        if not self.breaks:636            # no breakpoints; run without debugger overhead637            self.stop_trace()638            frame = sys._getframe().f_back639            while frame and frame is not self.botframe:640                del frame.f_trace641                frame = frame.f_back642            for frame, (trace_lines, trace_opcodes) in self.frame_trace_lines_opcodes.items():643                frame.f_trace_lines, frame.f_trace_opcodes = trace_lines, trace_opcodes644            if self.backend == 'monitoring':645                self.monitoring_tracer.update_local_events()646            self.frame_trace_lines_opcodes = {}647 648    def set_quit(self):649        """Set quitting attribute to True.650 651        Raises BdbQuit exception in the next call to a dispatch_*() method.652        """653        self.stopframe = self.botframe654        self.returnframe = None655        self.quitting = True656        self.stop_trace()657 658    # Derived classes and clients can call the following methods659    # to manipulate breakpoints.  These methods return an660    # error message if something went wrong, None if all is well.661    # Set_break prints out the breakpoint line and file:lineno.662    # Call self.get_*break*() to see the breakpoints or better663    # for bp in Breakpoint.bpbynumber: if bp: bp.bpprint().664 665    def _add_to_breaks(self, filename, lineno):666        """Add breakpoint to breaks, if not already there."""667        bp_linenos = self.breaks.setdefault(filename, [])668        if lineno not in bp_linenos:669            bp_linenos.append(lineno)670 671    def set_break(self, filename, lineno, temporary=False, cond=None,672                  funcname=None):673        """Set a new breakpoint for filename:lineno.674 675        If lineno doesn't exist for the filename, return an error message.676        The filename should be in canonical form.677        """678        filename = self.canonic(filename)679        import linecache # Import as late as possible680        line = linecache.getline(filename, lineno)681        if not line:682            return 'Line %s:%d does not exist' % (filename, lineno)683        self._add_to_breaks(filename, lineno)684        bp = Breakpoint(filename, lineno, temporary, cond, funcname)685        # After we set a new breakpoint, we need to search through all frames686        # and set f_trace to trace_dispatch if there could be a breakpoint in687        # that frame.688        frame = self.enterframe689        while frame:690            if self.break_anywhere(frame):691                frame.f_trace = self.trace_dispatch692            frame = frame.f_back693        return None694 695    def _load_breaks(self):696        """Apply all breakpoints (set in other instances) to this one.697 698        Populates this instance's breaks list from the Breakpoint class's699        list, which can have breakpoints set by another Bdb instance. This700        is necessary for interactive sessions to keep the breakpoints701        active across multiple calls to run().702        """703        for (filename, lineno) in Breakpoint.bplist.keys():704            self._add_to_breaks(filename, lineno)705 706    def _prune_breaks(self, filename, lineno):707        """Prune breakpoints for filename:lineno.708 709        A list of breakpoints is maintained in the Bdb instance and in710        the Breakpoint class.  If a breakpoint in the Bdb instance no711        longer exists in the Breakpoint class, then it's removed from the712        Bdb instance.713        """714        if (filename, lineno) not in Breakpoint.bplist:715            self.breaks[filename].remove(lineno)716        if not self.breaks[filename]:717            del self.breaks[filename]718 719    def clear_break(self, filename, lineno):720        """Delete breakpoints for filename:lineno.721 722        If no breakpoints were set, return an error message.723        """724        filename = self.canonic(filename)725        if filename not in self.breaks:726            return 'There are no breakpoints in %s' % filename727        if lineno not in self.breaks[filename]:728            return 'There is no breakpoint at %s:%d' % (filename, lineno)729        # If there's only one bp in the list for that file,line730        # pair, then remove the breaks entry731        for bp in Breakpoint.bplist[filename, lineno][:]:732            bp.deleteMe()733        self._prune_breaks(filename, lineno)734        return None735 736    def clear_bpbynumber(self, arg):737        """Delete a breakpoint by its index in Breakpoint.bpbynumber.738 739        If arg is invalid, return an error message.740        """741        try:742            bp = self.get_bpbynumber(arg)743        except ValueError as err:744            return str(err)745        bp.deleteMe()746        self._prune_breaks(bp.file, bp.line)747        return None748 749    def clear_all_file_breaks(self, filename):750        """Delete all breakpoints in filename.751 752        If none were set, return an error message.753        """754        filename = self.canonic(filename)755        if filename not in self.breaks:756            return 'There are no breakpoints in %s' % filename757        for line in self.breaks[filename]:758            blist = Breakpoint.bplist[filename, line]759            for bp in blist:760                bp.deleteMe()761        del self.breaks[filename]762        return None763 764    def clear_all_breaks(self):765        """Delete all existing breakpoints.766 767        If none were set, return an error message.768        """769        if not self.breaks:770            return 'There are no breakpoints'771        for bp in Breakpoint.bpbynumber:772            if bp:773                bp.deleteMe()774        self.breaks = {}775        return None776 777    def get_bpbynumber(self, arg):778        """Return a breakpoint by its index in Breakpoint.bybpnumber.779 780        For invalid arg values or if the breakpoint doesn't exist,781        raise a ValueError.782        """783        if not arg:784            raise ValueError('Breakpoint number expected')785        try:786            number = int(arg)787        except ValueError:788            raise ValueError('Non-numeric breakpoint number %s' % arg) from None789        try:790            bp = Breakpoint.bpbynumber[number]791        except IndexError:792            raise ValueError('Breakpoint number %d out of range' % number) from None793        if bp is None:794            raise ValueError('Breakpoint %d already deleted' % number)795        return bp796 797    def get_break(self, filename, lineno):798        """Return True if there is a breakpoint for filename:lineno."""799        filename = self.canonic(filename)800        return filename in self.breaks and \801            lineno in self.breaks[filename]802 803    def get_breaks(self, filename, lineno):804        """Return all breakpoints for filename:lineno.805 806        If no breakpoints are set, return an empty list.807        """808        filename = self.canonic(filename)809        return filename in self.breaks and \810            lineno in self.breaks[filename] and \811            Breakpoint.bplist[filename, lineno] or []812 813    def get_file_breaks(self, filename):814        """Return all lines with breakpoints for filename.815 816        If no breakpoints are set, return an empty list.817        """818        filename = self.canonic(filename)819        if filename in self.breaks:820            return self.breaks[filename]821        else:822            return []823 824    def get_all_breaks(self):825        """Return all breakpoints that are set."""826        return self.breaks827 828    # Derived classes and clients can call the following method829    # to get a data structure representing a stack trace.830 831    def get_stack(self, f, t):832        """Return a list of (frame, lineno) in a stack trace and a size.833 834        List starts with original calling frame, if there is one.835        Size may be number of frames above or below f.836        """837        stack = []838        if t and t.tb_frame is f:839            t = t.tb_next840        while f is not None:841            stack.append((f, f.f_lineno))842            if f is self.botframe:843                break844            f = f.f_back845        stack.reverse()846        i = max(0, len(stack) - 1)847        while t is not None:848            stack.append((t.tb_frame, t.tb_lineno))849            t = t.tb_next850        if f is None:851            i = max(0, len(stack) - 1)852        return stack, i853 854    def format_stack_entry(self, frame_lineno, lprefix=': '):855        """Return a string with information about a stack entry.856 857        The stack entry frame_lineno is a (frame, lineno) tuple.  The858        return string contains the canonical filename, the function name859        or '<lambda>', the input arguments, the return value, and the860        line of code (if it exists).861 862        """863        import linecache, reprlib864        frame, lineno = frame_lineno865        filename = self.canonic(frame.f_code.co_filename)866        s = '%s(%r)' % (filename, lineno)867        if frame.f_code.co_name:868            s += frame.f_code.co_name869        else:870            s += "<lambda>"871        s += '()'872        if '__return__' in frame.f_locals:873            rv = frame.f_locals['__return__']874            s += '->'875            s += reprlib.repr(rv)876        if lineno is not None:877            line = linecache.getline(filename, lineno, frame.f_globals)878            if line:879                s += lprefix + line.strip()880        else:881            s += f'{lprefix}Warning: lineno is None'882        return s883 884    def disable_current_event(self):885        """Disable the current event."""886        if self.backend == 'monitoring':887            self.monitoring_tracer.disable_current_event()888 889    def restart_events(self):890        """Restart all events."""891        if self.backend == 'monitoring':892            self.monitoring_tracer.restart_events()893 894    # The following methods can be called by clients to use895    # a debugger to debug a statement or an expression.896    # Both can be given as a string, or a code object.897 898    def run(self, cmd, globals=None, locals=None):899        """Debug a statement executed via the exec() function.900 901        globals defaults to __main__.dict; locals defaults to globals.902        """903        if globals is None:904            import __main__905            globals = __main__.__dict__906        if locals is None:907            locals = globals908        self.reset()909        if isinstance(cmd, str):910            cmd = compile(cmd, "<string>", "exec")911        self.start_trace()912        try:913            exec(cmd, globals, locals)914        except BdbQuit:915            pass916        finally:917            self.quitting = True918            self.stop_trace()919 920    def runeval(self, expr, globals=None, locals=None):921        """Debug an expression executed via the eval() function.922 923        globals defaults to __main__.dict; locals defaults to globals.924        """925        if globals is None:926            import __main__927            globals = __main__.__dict__928        if locals is None:929            locals = globals930        self.reset()931        self.start_trace()932        try:933            return eval(expr, globals, locals)934        except BdbQuit:935            pass936        finally:937            self.quitting = True938            self.stop_trace()939 940    def runctx(self, cmd, globals, locals):941        """For backwards-compatibility.  Defers to run()."""942        # B/W compatibility943        self.run(cmd, globals, locals)944 945    # This method is more useful to debug a single function call.946 947    def runcall(self, func, /, *args, **kwds):948        """Debug a single function call.949 950        Return the result of the function call.951        """952        self.reset()953        self.start_trace()954        res = None955        try:956            res = func(*args, **kwds)957        except BdbQuit:958            pass959        finally:960            self.quitting = True961            self.stop_trace()962        return res963 964 965def set_trace():966    """Start debugging with a Bdb instance from the caller's frame."""967    Bdb().set_trace()968 969 970class Breakpoint:971    """Breakpoint class.972 973    Implements temporary breakpoints, ignore counts, disabling and974    (re)-enabling, and conditionals.975 976    Breakpoints are indexed by number through bpbynumber and by977    the (file, line) tuple using bplist.  The former points to a978    single instance of class Breakpoint.  The latter points to a979    list of such instances since there may be more than one980    breakpoint per line.981 982    When creating a breakpoint, its associated filename should be983    in canonical form.  If funcname is defined, a breakpoint hit will be984    counted when the first line of that function is executed.  A985    conditional breakpoint always counts a hit.986    """987 988    # XXX Keeping state in the class is a mistake -- this means989    # you cannot have more than one active Bdb instance.990 991    next = 1        # Next bp to be assigned992    bplist = {}     # indexed by (file, lineno) tuple993    bpbynumber = [None] # Each entry is None or an instance of Bpt994                # index 0 is unused, except for marking an995                # effective break .... see effective()996 997    def __init__(self, file, line, temporary=False, cond=None, funcname=None):998        self.funcname = funcname999        # Needed if funcname is not None.1000        self.func_first_executable_line = None1001        self.file = file    # This better be in canonical form!1002        self.line = line1003        self.temporary = temporary1004        self.cond = cond1005        self.enabled = True1006        self.ignore = 01007        self.hits = 01008        self.number = Breakpoint.next1009        Breakpoint.next += 11010        # Build the two lists1011        self.bpbynumber.append(self)1012        if (file, line) in self.bplist:1013            self.bplist[file, line].append(self)1014        else:1015            self.bplist[file, line] = [self]1016 1017    @staticmethod1018    def clearBreakpoints():1019        Breakpoint.next = 11020        Breakpoint.bplist = {}1021        Breakpoint.bpbynumber = [None]1022 1023    def deleteMe(self):1024        """Delete the breakpoint from the list associated to a file:line.1025 1026        If it is the last breakpoint in that position, it also deletes1027        the entry for the file:line.1028        """1029 1030        index = (self.file, self.line)1031        self.bpbynumber[self.number] = None   # No longer in list1032        self.bplist[index].remove(self)1033        if not self.bplist[index]:1034            # No more bp for this f:l combo1035            del self.bplist[index]1036 1037    def enable(self):1038        """Mark the breakpoint as enabled."""1039        self.enabled = True1040 1041    def disable(self):1042        """Mark the breakpoint as disabled."""1043        self.enabled = False1044 1045    def bpprint(self, out=None):1046        """Print the output of bpformat().1047 1048        The optional out argument directs where the output is sent1049        and defaults to standard output.1050        """1051        if out is None:1052            out = sys.stdout1053        print(self.bpformat(), file=out)1054 1055    def bpformat(self):1056        """Return a string with information about the breakpoint.1057 1058        The information includes the breakpoint number, temporary1059        status, file:line position, break condition, number of times to1060        ignore, and number of times hit.1061 1062        """1063        if self.temporary:1064            disp = 'del  '1065        else:1066            disp = 'keep '1067        if self.enabled:1068            disp = disp + 'yes  '1069        else:1070            disp = disp + 'no   '1071        ret = '%-4dbreakpoint   %s at %s:%d' % (self.number, disp,1072                                                self.file, self.line)1073        if self.cond:1074            ret += '\n\tstop only if %s' % (self.cond,)1075        if self.ignore:1076            ret += '\n\tignore next %d hits' % (self.ignore,)1077        if self.hits:1078            if self.hits > 1:1079                ss = 's'1080            else:1081                ss = ''1082            ret += '\n\tbreakpoint already hit %d time%s' % (self.hits, ss)1083        return ret1084 1085    def __str__(self):1086        "Return a condensed description of the breakpoint."1087        return 'breakpoint %s at %s:%s' % (self.number, self.file, self.line)1088 1089# -----------end of Breakpoint class----------1090 1091 1092def checkfuncname(b, frame):1093    """Return True if break should happen here.1094 1095    Whether a break should happen depends on the way that b (the breakpoint)1096    was set.  If it was set via line number, check if b.line is the same as1097    the one in the frame.  If it was set via function name, check if this is1098    the right function and if it is on the first executable line.1099    """1100    if not b.funcname:1101        # Breakpoint was set via line number.1102        if b.line != frame.f_lineno:1103            # Breakpoint was set at a line with a def statement and the function1104            # defined is called: don't break.1105            return False1106        return True1107 1108    # Breakpoint set via function name.1109    if frame.f_code.co_name != b.funcname:1110        # It's not a function call, but rather execution of def statement.1111        return False1112 1113    # We are in the right frame.1114    if not b.func_first_executable_line:1115        # The function is entered for the 1st time.1116        b.func_first_executable_line = frame.f_lineno1117 1118    if b.func_first_executable_line != frame.f_lineno:1119        # But we are not at the first line number: don't break.1120        return False1121    return True1122 1123 1124def effective(file, line, frame):1125    """Return (active breakpoint, delete temporary flag) or (None, None) as1126       breakpoint to act upon.1127 1128       The "active breakpoint" is the first entry in bplist[line, file] (which1129       must exist) that is enabled, for which checkfuncname is True, and that1130       has neither a False condition nor a positive ignore count.  The flag,1131       meaning that a temporary breakpoint should be deleted, is False only1132       when the condiion cannot be evaluated (in which case, ignore count is1133       ignored).1134 1135       If no such entry exists, then (None, None) is returned.1136    """1137    possibles = Breakpoint.bplist[file, line]1138    for b in possibles:1139        if not b.enabled:1140            continue1141        if not checkfuncname(b, frame):1142            continue1143        # Count every hit when bp is enabled1144        b.hits += 11145        if not b.cond:1146            # If unconditional, and ignoring go on to next, else break1147            if b.ignore > 0:1148                b.ignore -= 11149                continue1150            else:1151                # breakpoint and marker that it's ok to delete if temporary1152                return (b, True)1153        else:1154            # Conditional bp.1155            # Ignore count applies only to those bpt hits where the1156            # condition evaluates to true.1157            try:1158                val = eval(b.cond, frame.f_globals, frame.f_locals)1159                if val:1160                    if b.ignore > 0:1161                        b.ignore -= 11162                        # continue1163                    else:1164                        return (b, True)1165                # else:1166                #   continue1167            except:1168                # if eval fails, most conservative thing is to stop on1169                # breakpoint regardless of ignore count.  Don't delete1170                # temporary, as another hint to user.1171                return (b, False)1172    return (None, None)1173 1174 1175# -------------------- testing --------------------1176 1177class Tdb(Bdb):1178    def user_call(self, frame, args):1179        name = frame.f_code.co_name1180        if not name: name = '???'1181        print('+++ call', name, args)1182    def user_line(self, frame):1183        import linecache1184        name = frame.f_code.co_name1185        if not name: name = '???'1186        fn = self.canonic(frame.f_code.co_filename)1187        line = linecache.getline(fn, frame.f_lineno, frame.f_globals)1188        print('+++', fn, frame.f_lineno, name, ':', line.strip())1189    def user_return(self, frame, retval):1190        print('+++ return', retval)1191    def user_exception(self, frame, exc_stuff):1192        print('+++ exception', exc_stuff)1193        self.set_continue()1194 1195def foo(n):1196    print('foo(', n, ')')1197    x = bar(n*10)1198    print('bar returned', x)1199 1200def bar(a):

Showing the first 1,200 of 1207 lines. Download the file for the rest.

codekingpro/portable-devtools · Team Ai