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