1"""Debugger basics""" 2 3import fnmatch 4import sys 5import os 6from inspect import CO_GENERATOR, CO_COROUTINE, CO_ASYNC_GENERATOR 7 8__all__ = ["BdbQuit", "Bdb", "Breakpoint"] 9 10GENERATOR_AND_COROUTINE_FLAGS = CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR 11 12 13class BdbQuit(Exception): 14 """Exception to give up completely.""" 15 16 17class Bdb: 18 """Generic Python debugger base class. 19 20 This class takes care of details of the trace facility; 21 a derived class should implement user interaction. 22 The standard debugger class (pdb.Pdb) is an example. 23 24 The optional skip argument must be an iterable of glob-style 25 module name patterns. The debugger will not step into frames 26 that originate in a module that matches one of these patterns. 27 Whether a frame is considered to originate in a certain module 28 is determined by the __name__ in the frame globals. 29 """ 30 31 def __init__(self, skip=None): 32 self.skip = set(skip) if skip else None 33 self.breaks = {} 34 self.fncache = {} 35 self.frame_returning = None 36 37 def canonic(self, filename): 38 """Return canonical form of filename. 39 40 For real filenames, the canonical form is a case-normalized (on 41 case insenstive filesystems) absolute path. 'Filenames' with 42 angle brackets, such as "<stdin>", generated in interactive 43 mode, are returned unchanged. 44 """ 45 if filename == "<" + filename[1:-1] + ">": 46 return filename 47 canonic = self.fncache.get(filename) 48 if not canonic: 49 canonic = os.path.abspath(filename) 50 canonic = os.path.normcase(canonic) 51 self.fncache[filename] = canonic 52 return canonic 53 54 def reset(self): 55 """Set values of attributes as ready to start debugging.""" 56 import linecache 57 linecache.checkcache() 58 self.botframe = None 59 self._set_stopinfo(None, None) 60 61 def trace_dispatch(self, frame, event, arg): 62 """Dispatch a trace function for debugged frames based on the event. 63 64 This function is installed as the trace function for debugged 65 frames. Its return value is the new trace function, which is 66 usually itself. The default implementation decides how to 67 dispatch a frame, depending on the type of event (passed in as a 68 string) that is about to be executed. 69 70 The event can be one of the following: 71 line: A new line of code is going to be executed. 72 call: A function is about to be called or another code block 73 is entered. 74 return: A function or other code block is about to return. 75 exception: An exception has occurred. 76 c_call: A C function is about to be called. 77 c_return: A C function has returned. 78 c_exception: A C function has raised an exception. 79 80 For the Python events, specialized functions (see the dispatch_*() 81 methods) are called. For the C events, no action is taken. 82 83 The arg parameter depends on the previous event. 84 """ 85 if self.quitting: 86 return # None 87 if event == 'line': 88 return self.dispatch_line(frame) 89 if event == 'call': 90 return self.dispatch_call(frame, arg) 91 if event == 'return': 92 return self.dispatch_return(frame, arg) 93 if event == 'exception': 94 return self.dispatch_exception(frame, arg) 95 if event == 'c_call': 96 return self.trace_dispatch 97 if event == 'c_exception': 98 return self.trace_dispatch 99 if event == 'c_return': 100 return self.trace_dispatch 101 print('bdb.Bdb.dispatch: unknown debugging event:', repr(event)) 102 return self.trace_dispatch 103 104 def dispatch_line(self, frame): 105 """Invoke user function and return trace function for line event. 106 107 If the debugger stops on the current line, invoke 108 self.user_line(). Raise BdbQuit if self.quitting is set. 109 Return self.trace_dispatch to continue tracing in this scope. 110 """ 111 if self.stop_here(frame) or self.break_here(frame): 112 self.user_line(frame) 113 if self.quitting: raise BdbQuit 114 return self.trace_dispatch 115 116 def dispatch_call(self, frame, arg): 117 """Invoke user function and return trace function for call event. 118 119 If the debugger stops on this function call, invoke 120 self.user_call(). Raise BbdQuit if self.quitting is set. 121 Return self.trace_dispatch to continue tracing in this scope. 122 """ 123 # XXX 'arg' is no longer used 124 if self.botframe is None: 125 # First call of dispatch since reset() 126 self.botframe = frame.f_back # (CT) Note that this may also be None! 127 return self.trace_dispatch 128 if not (self.stop_here(frame) or self.break_anywhere(frame)): 129 # No need to trace this function 130 return # None 131 # Ignore call events in generator except when stepping. 132 if self.stopframe and frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS: 133 return self.trace_dispatch 134 self.user_call(frame, arg) 135 if self.quitting: raise BdbQuit 136 return self.trace_dispatch 137 138 def dispatch_return(self, frame, arg): 139 """Invoke user function and return trace function for return event. 140 141 If the debugger stops on this function return, invoke 142 self.user_return(). Raise BdbQuit if self.quitting is set. 143 Return self.trace_dispatch to continue tracing in this scope. 144 """ 145 if self.stop_here(frame) or frame == self.returnframe: 146 # Ignore return events in generator except when stepping. 147 if self.stopframe and frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS: 148 return self.trace_dispatch 149 try: 150 self.frame_returning = frame 151 self.user_return(frame, arg) 152 finally: 153 self.frame_returning = None 154 if self.quitting: raise BdbQuit 155 # The user issued a 'next' or 'until' command. 156 if self.stopframe is frame and self.stoplineno != -1: 157 self._set_stopinfo(None, None) 158 return self.trace_dispatch 159 160 def dispatch_exception(self, frame, arg): 161 """Invoke user function and return trace function for exception event. 162 163 If the debugger stops on this exception, invoke 164 self.user_exception(). Raise BdbQuit if self.quitting is set. 165 Return self.trace_dispatch to continue tracing in this scope. 166 """ 167 if self.stop_here(frame): 168 # When stepping with next/until/return in a generator frame, skip 169 # the internal StopIteration exception (with no traceback) 170 # triggered by a subiterator run with the 'yield from' statement. 171 if not (frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS 172 and arg[0] is StopIteration and arg[2] is None): 173 self.user_exception(frame, arg) 174 if self.quitting: raise BdbQuit 175 # Stop at the StopIteration or GeneratorExit exception when the user 176 # has set stopframe in a generator by issuing a return command, or a 177 # next/until command at the last statement in the generator before the 178 # exception. 179 elif (self.stopframe and frame is not self.stopframe 180 and self.stopframe.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS 181 and arg[0] in (StopIteration, GeneratorExit)): 182 self.user_exception(frame, arg) 183 if self.quitting: raise BdbQuit 184 185 return self.trace_dispatch 186 187 # Normally derived classes don't override the following 188 # methods, but they may if they want to redefine the 189 # definition of stopping and breakpoints. 190 191 def is_skipped_module(self, module_name): 192 "Return True if module_name matches any skip pattern." 193 for pattern in self.skip: 194 if fnmatch.fnmatch(module_name, pattern): 195 return True 196 return False 197 198 def stop_here(self, frame): 199 "Return True if frame is below the starting frame in the stack." 200 # (CT) stopframe may now also be None, see dispatch_call. 201 # (CT) the former test for None is therefore removed from here. 202 if self.skip and \ 203 self.is_skipped_module(frame.f_globals.get('__name__')): 204 return False 205 if frame is self.stopframe: 206 if self.stoplineno == -1: 207 return False 208 return frame.f_lineno >= self.stoplineno 209 if not self.stopframe: 210 return True 211 return False 212 213 def break_here(self, frame): 214 """Return True if there is an effective breakpoint for this line. 215 216 Check for line or function breakpoint and if in effect. 217 Delete temporary breakpoints if effective() says to. 218 """ 219 filename = self.canonic(frame.f_code.co_filename) 220 if filename not in self.breaks: 221 return False 222 lineno = frame.f_lineno 223 if lineno not in self.breaks[filename]: 224 # The line itself has no breakpoint, but maybe the line is the 225 # first line of a function with breakpoint set by function name. 226 lineno = frame.f_code.co_firstlineno 227 if lineno not in self.breaks[filename]: 228 return False 229 230 # flag says ok to delete temp. bp 231 (bp, flag) = effective(filename, lineno, frame) 232 if bp: 233 self.currentbp = bp.number 234 if (flag and bp.temporary): 235 self.do_clear(str(bp.number)) 236 return True 237 else: 238 return False 239 240 def do_clear(self, arg): 241 """Remove temporary breakpoint. 242 243 Must implement in derived classes or get NotImplementedError. 244 """ 245 raise NotImplementedError("subclass of bdb must implement do_clear()") 246 247 def break_anywhere(self, frame): 248 """Return True if there is any breakpoint for frame's filename. 249 """ 250 return self.canonic(frame.f_code.co_filename) in self.breaks 251 252 # Derived classes should override the user_* methods 253 # to gain control. 254 255 def user_call(self, frame, argument_list): 256 """Called if we might stop in a function.""" 257 pass 258 259 def user_line(self, frame): 260 """Called when we stop or break at a line.""" 261 pass 262 263 def user_return(self, frame, return_value): 264 """Called when a return trap is set here.""" 265 pass 266 267 def user_exception(self, frame, exc_info): 268 """Called when we stop on an exception.""" 269 pass 270 271 def _set_stopinfo(self, stopframe, returnframe, stoplineno=0): 272 """Set the attributes for stopping. 273 274 If stoplineno is greater than or equal to 0, then stop at line 275 greater than or equal to the stopline. If stoplineno is -1, then 276 don't stop at all. 277 """ 278 self.stopframe = stopframe 279 self.returnframe = returnframe 280 self.quitting = False 281 # stoplineno >= 0 means: stop at line >= the stoplineno 282 # stoplineno -1 means: don't stop at all 283 self.stoplineno = stoplineno 284 285 # Derived classes and clients can call the following methods 286 # to affect the stepping state. 287 288 def set_until(self, frame, lineno=None): 289 """Stop when the line with the lineno greater than the current one is 290 reached or when returning from current frame.""" 291 # the name "until" is borrowed from gdb 292 if lineno is None: 293 lineno = frame.f_lineno + 1 294 self._set_stopinfo(frame, frame, lineno) 295 296 def set_step(self): 297 """Stop after one line of code.""" 298 # Issue #13183: pdb skips frames after hitting a breakpoint and running 299 # step commands. 300 # Restore the trace function in the caller (that may not have been set 301 # for performance reasons) when returning from the current frame. 302 if self.frame_returning: 303 caller_frame = self.frame_returning.f_back 304 if caller_frame and not caller_frame.f_trace: 305 caller_frame.f_trace = self.trace_dispatch 306 self._set_stopinfo(None, None) 307 308 def set_next(self, frame): 309 """Stop on the next line in or below the given frame.""" 310 self._set_stopinfo(frame, None) 311 312 def set_return(self, frame): 313 """Stop when returning from the given frame.""" 314 if frame.f_code.co_flags & GENERATOR_AND_COROUTINE_FLAGS: 315 self._set_stopinfo(frame, None, -1) 316 else: 317 self._set_stopinfo(frame.f_back, frame) 318 319 def set_trace(self, frame=None): 320 """Start debugging from frame. 321 322 If frame is not specified, debugging starts from caller's frame. 323 """ 324 if frame is None: 325 frame = sys._getframe().f_back 326 self.reset() 327 while frame: 328 frame.f_trace = self.trace_dispatch 329 self.botframe = frame 330 frame = frame.f_back 331 self.set_step() 332 sys.settrace(self.trace_dispatch) 333 334 def set_continue(self): 335 """Stop only at breakpoints or when finished. 336 337 If there are no breakpoints, set the system trace function to None. 338 """ 339 # Don't stop except at breakpoints or when finished 340 self._set_stopinfo(self.botframe, None, -1) 341 if not self.breaks: 342 # no breakpoints; run without debugger overhead 343 sys.settrace(None) 344 frame = sys._getframe().f_back 345 while frame and frame is not self.botframe: 346 del frame.f_trace 347 frame = frame.f_back 348 349 def set_quit(self): 350 """Set quitting attribute to True. 351 352 Raises BdbQuit exception in the next call to a dispatch_*() method. 353 """ 354 self.stopframe = self.botframe 355 self.returnframe = None 356 self.quitting = True 357 sys.settrace(None) 358 359 # Derived classes and clients can call the following methods 360 # to manipulate breakpoints. These methods return an 361 # error message if something went wrong, None if all is well. 362 # Set_break prints out the breakpoint line and file:lineno. 363 # Call self.get_*break*() to see the breakpoints or better 364 # for bp in Breakpoint.bpbynumber: if bp: bp.bpprint(). 365 366 def set_break(self, filename, lineno, temporary=False, cond=None, 367 funcname=None): 368 """Set a new breakpoint for filename:lineno. 369 370 If lineno doesn't exist for the filename, return an error message. 371 The filename should be in canonical form. 372 """ 373 filename = self.canonic(filename) 374 import linecache # Import as late as possible 375 line = linecache.getline(filename, lineno) 376 if not line: 377 return 'Line %s:%d does not exist' % (filename, lineno) 378 list = self.breaks.setdefault(filename, []) 379 if lineno not in list: 380 list.append(lineno) 381 bp = Breakpoint(filename, lineno, temporary, cond, funcname) 382 return None 383 384 def _prune_breaks(self, filename, lineno): 385 """Prune breakpoints for filname:lineno. 386 387 A list of breakpoints is maintained in the Bdb instance and in 388 the Breakpoint class. If a breakpoint in the Bdb instance no 389 longer exists in the Breakpoint class, then it's removed from the 390 Bdb instance. 391 """ 392 if (filename, lineno) not in Breakpoint.bplist: 393 self.breaks[filename].remove(lineno) 394 if not self.breaks[filename]: 395 del self.breaks[filename] 396 397 def clear_break(self, filename, lineno): 398 """Delete breakpoints for filename:lineno. 399 400 If no breakpoints were set, return an error message. 401 """ 402 filename = self.canonic(filename) 403 if filename not in self.breaks: 404 return 'There are no breakpoints in %s' % filename 405 if lineno not in self.breaks[filename]: 406 return 'There is no breakpoint at %s:%d' % (filename, lineno) 407 # If there's only one bp in the list for that file,line 408 # pair, then remove the breaks entry 409 for bp in Breakpoint.bplist[filename, lineno][:]: 410 bp.deleteMe() 411 self._prune_breaks(filename, lineno) 412 return None 413 414 def clear_bpbynumber(self, arg): 415 """Delete a breakpoint by its index in Breakpoint.bpbynumber. 416 417 If arg is invalid, return an error message. 418 """ 419 try: 420 bp = self.get_bpbynumber(arg) 421 except ValueError as err: 422 return str(err) 423 bp.deleteMe() 424 self._prune_breaks(bp.file, bp.line) 425 return None 426 427 def clear_all_file_breaks(self, filename): 428 """Delete all breakpoints in filename. 429 430 If none were set, return an error message. 431 """ 432 filename = self.canonic(filename) 433 if filename not in self.breaks: 434 return 'There are no breakpoints in %s' % filename 435 for line in self.breaks[filename]: 436 blist = Breakpoint.bplist[filename, line] 437 for bp in blist: 438 bp.deleteMe() 439 del self.breaks[filename] 440 return None 441 442 def clear_all_breaks(self): 443 """Delete all existing breakpoints. 444 445 If none were set, return an error message. 446 """ 447 if not self.breaks: 448 return 'There are no breakpoints' 449 for bp in Breakpoint.bpbynumber: 450 if bp: 451 bp.deleteMe() 452 self.breaks = {} 453 return None 454 455 def get_bpbynumber(self, arg): 456 """Return a breakpoint by its index in Breakpoint.bybpnumber. 457 458 For invalid arg values or if the breakpoint doesn't exist, 459 raise a ValueError. 460 """ 461 if not arg: 462 raise ValueError('Breakpoint number expected') 463 try: 464 number = int(arg) 465 except ValueError: 466 raise ValueError('Non-numeric breakpoint number %s' % arg) from None 467 try: 468 bp = Breakpoint.bpbynumber[number] 469 except IndexError: 470 raise ValueError('Breakpoint number %d out of range' % number) from None 471 if bp is None: 472 raise ValueError('Breakpoint %d already deleted' % number) 473 return bp 474 475 def get_break(self, filename, lineno): 476 """Return True if there is a breakpoint for filename:lineno.""" 477 filename = self.canonic(filename) 478 return filename in self.breaks and \ 479 lineno in self.breaks[filename] 480 481 def get_breaks(self, filename, lineno): 482 """Return all breakpoints for filename:lineno. 483 484 If no breakpoints are set, return an empty list. 485 """ 486 filename = self.canonic(filename) 487 return filename in self.breaks and \ 488 lineno in self.breaks[filename] and \ 489 Breakpoint.bplist[filename, lineno] or [] 490 491 def get_file_breaks(self, filename): 492 """Return all lines with breakpoints for filename. 493 494 If no breakpoints are set, return an empty list. 495 """ 496 filename = self.canonic(filename) 497 if filename in self.breaks: 498 return self.breaks[filename] 499 else: 500 return [] 501 502 def get_all_breaks(self): 503 """Return all breakpoints that are set.""" 504 return self.breaks 505 506 # Derived classes and clients can call the following method 507 # to get a data structure representing a stack trace. 508 509 def get_stack(self, f, t): 510 """Return a list of (frame, lineno) in a stack trace and a size. 511 512 List starts with original calling frame, if there is one. 513 Size may be number of frames above or below f. 514 """ 515 stack = [] 516 if t and t.tb_frame is f: 517 t = t.tb_next 518 while f is not None: 519 stack.append((f, f.f_lineno)) 520 if f is self.botframe: 521 break 522 f = f.f_back 523 stack.reverse() 524 i = max(0, len(stack) - 1) 525 while t is not None: 526 stack.append((t.tb_frame, t.tb_lineno)) 527 t = t.tb_next 528 if f is None: 529 i = max(0, len(stack) - 1) 530 return stack, i 531 532 def format_stack_entry(self, frame_lineno, lprefix=': '): 533 """Return a string with information about a stack entry. 534 535 The stack entry frame_lineno is a (frame, lineno) tuple. The 536 return string contains the canonical filename, the function name 537 or '<lambda>', the input arguments, the return value, and the 538 line of code (if it exists). 539 540 """ 541 import linecache, reprlib 542 frame, lineno = frame_lineno 543 filename = self.canonic(frame.f_code.co_filename) 544 s = '%s(%r)' % (filename, lineno) 545 if frame.f_code.co_name: 546 s += frame.f_code.co_name 547 else: 548 s += "<lambda>" 549 s += '()' 550 if '__return__' in frame.f_locals: 551 rv = frame.f_locals['__return__'] 552 s += '->' 553 s += reprlib.repr(rv) 554 line = linecache.getline(filename, lineno, frame.f_globals) 555 if line: 556 s += lprefix + line.strip() 557 return s 558 559 # The following methods can be called by clients to use 560 # a debugger to debug a statement or an expression. 561 # Both can be given as a string, or a code object. 562 563 def run(self, cmd, globals=None, locals=None): 564 """Debug a statement executed via the exec() function. 565 566 globals defaults to __main__.dict; locals defaults to globals. 567 """ 568 if globals is None: 569 import __main__ 570 globals = __main__.__dict__ 571 if locals is None: 572 locals = globals 573 self.reset() 574 if isinstance(cmd, str): 575 cmd = compile(cmd, "<string>", "exec") 576 sys.settrace(self.trace_dispatch) 577 try: 578 exec(cmd, globals, locals) 579 except BdbQuit: 580 pass 581 finally: 582 self.quitting = True 583 sys.settrace(None) 584 585 def runeval(self, expr, globals=None, locals=None): 586 """Debug an expression executed via the eval() function. 587 588 globals defaults to __main__.dict; locals defaults to globals. 589 """ 590 if globals is None: 591 import __main__ 592 globals = __main__.__dict__ 593 if locals is None: 594 locals = globals 595 self.reset() 596 sys.settrace(self.trace_dispatch) 597 try: 598 return eval(expr, globals, locals) 599 except BdbQuit: 600 pass 601 finally: 602 self.quitting = True 603 sys.settrace(None) 604 605 def runctx(self, cmd, globals, locals): 606 """For backwards-compatibility. Defers to run().""" 607 # B/W compatibility 608 self.run(cmd, globals, locals) 609 610 # This method is more useful to debug a single function call. 611 612 def runcall(*args, **kwds): 613 """Debug a single function call. 614 615 Return the result of the function call. 616 """ 617 if len(args) >= 2: 618 self, func, *args = args 619 elif not args: 620 raise TypeError("descriptor 'runcall' of 'Bdb' object " 621 "needs an argument") 622 elif 'func' in kwds: 623 func = kwds.pop('func') 624 self, *args = args 625 else: 626 raise TypeError('runcall expected at least 1 positional argument, ' 627 'got %d' % (len(args)-1)) 628 629 self.reset() 630 sys.settrace(self.trace_dispatch) 631 res = None 632 try: 633 res = func(*args, **kwds) 634 except BdbQuit: 635 pass 636 finally: 637 self.quitting = True 638 sys.settrace(None) 639 return res 640 641 642def set_trace(): 643 """Start debugging with a Bdb instance from the caller's frame.""" 644 Bdb().set_trace() 645 646 647class Breakpoint: 648 """Breakpoint class. 649 650 Implements temporary breakpoints, ignore counts, disabling and 651 (re)-enabling, and conditionals. 652 653 Breakpoints are indexed by number through bpbynumber and by 654 the (file, line) tuple using bplist. The former points to a 655 single instance of class Breakpoint. The latter points to a 656 list of such instances since there may be more than one 657 breakpoint per line. 658 659 When creating a breakpoint, its associated filename should be 660 in canonical form. If funcname is defined, a breakpoint hit will be 661 counted when the first line of that function is executed. A 662 conditional breakpoint always counts a hit. 663 """ 664 665 # XXX Keeping state in the class is a mistake -- this means 666 # you cannot have more than one active Bdb instance. 667 668 next = 1 # Next bp to be assigned 669 bplist = {} # indexed by (file, lineno) tuple 670 bpbynumber = [None] # Each entry is None or an instance of Bpt 671 # index 0 is unused, except for marking an 672 # effective break .... see effective() 673 674 def __init__(self, file, line, temporary=False, cond=None, funcname=None): 675 self.funcname = funcname 676 # Needed if funcname is not None. 677 self.func_first_executable_line = None 678 self.file = file # This better be in canonical form! 679 self.line = line 680 self.temporary = temporary 681 self.cond = cond 682 self.enabled = True 683 self.ignore = 0 684 self.hits = 0 685 self.number = Breakpoint.next 686 Breakpoint.next += 1 687 # Build the two lists 688 self.bpbynumber.append(self) 689 if (file, line) in self.bplist: 690 self.bplist[file, line].append(self) 691 else: 692 self.bplist[file, line] = [self] 693 694 def deleteMe(self): 695 """Delete the breakpoint from the list associated to a file:line. 696 697 If it is the last breakpoint in that position, it also deletes 698 the entry for the file:line. 699 """ 700 701 index = (self.file, self.line) 702 self.bpbynumber[self.number] = None # No longer in list 703 self.bplist[index].remove(self) 704 if not self.bplist[index]: 705 # No more bp for this f:l combo 706 del self.bplist[index] 707 708 def enable(self): 709 """Mark the breakpoint as enabled.""" 710 self.enabled = True 711 712 def disable(self): 713 """Mark the breakpoint as disabled.""" 714 self.enabled = False 715 716 def bpprint(self, out=None): 717 """Print the output of bpformat(). 718 719 The optional out argument directs where the output is sent 720 and defaults to standard output. 721 """ 722 if out is None: 723 out = sys.stdout 724 print(self.bpformat(), file=out) 725 726 def bpformat(self): 727 """Return a string with information about the breakpoint. 728 729 The information includes the breakpoint number, temporary 730 status, file:line position, break condition, number of times to 731 ignore, and number of times hit. 732 733 """ 734 if self.temporary: 735 disp = 'del ' 736 else: 737 disp = 'keep ' 738 if self.enabled: 739 disp = disp + 'yes ' 740 else: 741 disp = disp + 'no ' 742 ret = '%-4dbreakpoint %s at %s:%d' % (self.number, disp, 743 self.file, self.line) 744 if self.cond: 745 ret += '\n\tstop only if %s' % (self.cond,) 746 if self.ignore: 747 ret += '\n\tignore next %d hits' % (self.ignore,) 748 if self.hits: 749 if self.hits > 1: 750 ss = 's' 751 else: 752 ss = '' 753 ret += '\n\tbreakpoint already hit %d time%s' % (self.hits, ss) 754 return ret 755 756 def __str__(self): 757 "Return a condensed description of the breakpoint." 758 return 'breakpoint %s at %s:%s' % (self.number, self.file, self.line) 759 760# -----------end of Breakpoint class---------- 761 762 763def checkfuncname(b, frame): 764 """Return True if break should happen here. 765 766 Whether a break should happen depends on the way that b (the breakpoint) 767 was set. If it was set via line number, check if b.line is the same as 768 the one in the frame. If it was set via function name, check if this is 769 the right function and if it is on the first executable line. 770 """ 771 if not b.funcname: 772 # Breakpoint was set via line number. 773 if b.line != frame.f_lineno: 774 # Breakpoint was set at a line with a def statement and the function 775 # defined is called: don't break. 776 return False 777 return True 778 779 # Breakpoint set via function name. 780 if frame.f_code.co_name != b.funcname: 781 # It's not a function call, but rather execution of def statement. 782 return False 783 784 # We are in the right frame. 785 if not b.func_first_executable_line: 786 # The function is entered for the 1st time. 787 b.func_first_executable_line = frame.f_lineno 788 789 if b.func_first_executable_line != frame.f_lineno: 790 # But we are not at the first line number: don't break. 791 return False 792 return True 793 794 795# Determines if there is an effective (active) breakpoint at this 796# line of code. Returns breakpoint number or 0 if none 797def effective(file, line, frame): 798 """Determine which breakpoint for this file:line is to be acted upon. 799 800 Called only if we know there is a breakpoint at this location. Return 801 the breakpoint that was triggered and a boolean that indicates if it is 802 ok to delete a temporary breakpoint. Return (None, None) if there is no 803 matching breakpoint. 804 """ 805 possibles = Breakpoint.bplist[file, line] 806 for b in possibles: 807 if not b.enabled: 808 continue 809 if not checkfuncname(b, frame): 810 continue 811 # Count every hit when bp is enabled 812 b.hits += 1 813 if not b.cond: 814 # If unconditional, and ignoring go on to next, else break 815 if b.ignore > 0: 816 b.ignore -= 1 817 continue 818 else: 819 # breakpoint and marker that it's ok to delete if temporary 820 return (b, True) 821 else: 822 # Conditional bp. 823 # Ignore count applies only to those bpt hits where the 824 # condition evaluates to true. 825 try: 826 val = eval(b.cond, frame.f_globals, frame.f_locals) 827 if val: 828 if b.ignore > 0: 829 b.ignore -= 1 830 # continue 831 else: 832 return (b, True) 833 # else: 834 # continue 835 except: 836 # if eval fails, most conservative thing is to stop on 837 # breakpoint regardless of ignore count. Don't delete 838 # temporary, as another hint to user. 839 return (b, False) 840 return (None, None) 841 842 843# -------------------- testing -------------------- 844 845class Tdb(Bdb): 846 def user_call(self, frame, args): 847 name = frame.f_code.co_name 848 if not name: name = '???' 849 print('+++ call', name, args) 850 def user_line(self, frame): 851 import linecache 852 name = frame.f_code.co_name 853 if not name: name = '???' 854 fn = self.canonic(frame.f_code.co_filename) 855 line = linecache.getline(fn, frame.f_lineno, frame.f_globals) 856 print('+++', fn, frame.f_lineno, name, ':', line.strip()) 857 def user_return(self, frame, retval): 858 print('+++ return', retval) 859 def user_exception(self, frame, exc_stuff): 860 print('+++ exception', exc_stuff) 861 self.set_continue() 862 863def foo(n): 864 print('foo(', n, ')') 865 x = bar(n*10) 866 print('bar returned', x) 867 868def bar(a): 869 print('bar(', a, ')') 870 return a/2 871 872def test(): 873 t = Tdb() 874 t.run('import bdb; bdb.foo(10)') 875