| OLD | NEW |
| 1 """Debugger basics""" | 1 """Debugger basics""" |
| 2 | 2 |
| 3 import fnmatch | 3 import fnmatch |
| 4 import sys | 4 import sys |
| 5 import os | 5 import os |
| 6 import inspect |
| 7 import linecache |
| 8 import token |
| 9 import tokenize |
| 10 import imp |
| 11 import importlib |
| 12 import pprint |
| 13 from operator import itemgetter, attrgetter |
| 6 | 14 |
| 7 __all__ = ["BdbQuit", "Bdb", "Breakpoint"] | 15 __all__ = ["BdbQuit", "Bdb", "Breakpoint"] |
| 16 |
| 17 # A dictionary mapping a filename to a ModuleSource instance. |
| 18 _modules = {} |
| 19 _fncache = {} |
| 20 |
| 21 def canonic(filename): |
| 22 if filename == "<" + filename[1:-1] + ">": |
| 23 return filename |
| 24 canonic = _fncache.get(filename) |
| 25 if not canonic: |
| 26 canonic = os.path.abspath(filename) |
| 27 canonic = os.path.normcase(canonic) |
| 28 _fncache[filename] = canonic |
| 29 return canonic |
| 30 |
| 31 def getfilename(module_name, path=None, inpackage=None): |
| 32 """Return the file name of module_name.""" |
| 33 if module_name in sys.modules: |
| 34 return getattr(sys.modules[module_name], '__file__', None) |
| 35 |
| 36 if inpackage is not None: |
| 37 fullmodule = '{}.{}'.format(inpackage, module_name) |
| 38 else: |
| 39 fullmodule = module_name |
| 40 |
| 41 i = module_name.rfind('.') |
| 42 if i >= 0: |
| 43 package = module_name[:i] |
| 44 submodule = module_name[i+1:] |
| 45 parent = getfilename(package, path, inpackage) |
| 46 if parent is None: |
| 47 return None |
| 48 if inpackage is not None: |
| 49 package = '{}.{}'.format(inpackage, package) |
| 50 return getfilename(submodule, [os.path.dirname(parent)], package) |
| 51 |
| 52 if inpackage is not None: |
| 53 search_path = path |
| 54 else: |
| 55 search_path = sys.path |
| 56 try: |
| 57 loader = importlib.find_loader(fullmodule, search_path) |
| 58 return loader.get_filename(fullmodule) |
| 59 except (AttributeError, ImportError): |
| 60 return None |
| 61 |
| 62 def funcname_breakpoint(funcname, filename=None, frame=None): |
| 63 """Return the filename and the line of the first statement of funcname. |
| 64 |
| 65 Fail with BdbError when filename and line cannot be found. |
| 66 """ |
| 67 if not filename and not frame: |
| 68 raise BdbError('Error at funcname_breakpoint: invalid arguments.') |
| 69 |
| 70 if filename: |
| 71 filename = canonic(filename) |
| 72 if filename not in _modules: |
| 73 _modules[filename] = ModuleSource(filename) |
| 74 module_src = _modules[filename] |
| 75 func_lno = module_src.get_func_lno(funcname) |
| 76 lineno = module_src.get_actual_bp(func_lno)[1] |
| 77 if not lineno: |
| 78 raise BdbError('Bad function name: "{}".'.format(funcname)) |
| 79 return filename, lineno |
| 80 |
| 81 # frame is not None |
| 82 try: |
| 83 func = eval(funcname, frame.f_globals) |
| 84 except: |
| 85 # funcname is defined in a module not yet (fully) imported |
| 86 module = inspect.getmodule(frame) |
| 87 filename, lineno = FunctionQualifiedName(funcname, |
| 88 module).get_fileline() |
| 89 if not filename: |
| 90 raise BdbError('Bad name: "{}".'.format(funcname)) |
| 91 return filename, lineno |
| 92 else: |
| 93 try: |
| 94 filename = inspect.getfile(func) |
| 95 except TypeError: |
| 96 raise BdbError('Cannot set a breakpoint at "{}"'.format(funcname)) |
| 97 return funcname_breakpoint(funcname, filename) |
| 98 |
| 99 |
| 100 class BdbError(Exception): |
| 101 """Generic bdb exception.""" |
| 8 | 102 |
| 9 class BdbQuit(Exception): | 103 class BdbQuit(Exception): |
| 10 """Exception to give up completely.""" | 104 """Exception to give up completely.""" |
| 11 | 105 |
| 106 class LineSet: |
| 107 """A group of consecutive lines. |
| 108 |
| 109 LineSet is defined by (start, last, ltype, func_lno) where start is the |
| 110 first line number of the lineset, last the last line number, ltype its type |
| 111 and func_lno is the first line number of the function definition that the |
| 112 LineSet belongs to or zero when the group of lines is defined outside a |
| 113 function. |
| 114 |
| 115 """ |
| 116 |
| 117 # A LineSet type: a function definition possibly spanning multiple physical |
| 118 # lines, or a group of consecutive lines, or a logical line that spans |
| 119 # multiple physical lines. |
| 120 LINE_DEF, LINE_GROUP, LINE_MULTI = tuple(range(3)) |
| 121 |
| 122 def __init__(self): |
| 123 self.reset() |
| 124 |
| 125 def reset(self, start=0, ltype=LINE_GROUP, func_lno=0, last=0): |
| 126 self.start = start |
| 127 self.last = last |
| 128 self.ltype = ltype |
| 129 self.func_lno = func_lno |
| 130 |
| 131 def add(self, module): |
| 132 """Add the lineset to the ModuleSource instance.""" |
| 133 if self.start and self.last: |
| 134 lines = (module.functions_lines |
| 135 if self.func_lno else module.module_lines) |
| 136 lines.append((self.start, self.last, self.ltype, self.func_lno)) |
| 137 self.reset(func_lno=self.func_lno) |
| 138 |
| 139 class ModuleSource: |
| 140 """A parsed module source. |
| 141 |
| 142 Instance attributes: |
| 143 function_lineno: a dictionary mapping function and method names to the |
| 144 first line number of their definition |
| 145 firstlineno: a dictionary mapping the first line number of a callable to
: |
| 146 - the first line number of the enclosing function, when the |
| 147 callable is nested in a function definition |
| 148 - zero (by convention, zero is the module first line number for |
| 149 breakpoints set at module level), when a class not nested in a |
| 150 function definition |
| 151 - the first line number of the callable otherwise |
| 152 last_line: the last line number of the module |
| 153 module_lines: a list of the module LineSet tuples |
| 154 functions_lines: a list of the functions LineSet tuples |
| 155 """ |
| 156 |
| 157 def __init__(self, filename): |
| 158 self.filename = filename |
| 159 self.stat = self.get_stat() |
| 160 self.parse() |
| 161 |
| 162 def reset(self): |
| 163 """Update ModuleSource after the file has been modified.""" |
| 164 stat = self.get_stat() |
| 165 if not self.stat or not stat: |
| 166 return |
| 167 if (self.stat.st_size == stat.st_size and |
| 168 self.stat.st_mtime == stat.st_mtime): |
| 169 return |
| 170 self.stat = stat |
| 171 |
| 172 # The file has been modified, attempt to reload its module. |
| 173 for module in sys.modules.values(): |
| 174 fname = getattr(module, '__file__', None) |
| 175 if fname and canonic(fname) == self.filename: |
| 176 try: |
| 177 imp.reload(module) |
| 178 except: |
| 179 pass |
| 180 break |
| 181 |
| 182 self.parse() |
| 183 |
| 184 def get_stat(self): |
| 185 try: |
| 186 return os.stat(self.filename) |
| 187 except os.error: |
| 188 return None |
| 189 |
| 190 def get_func_lno(self, funcname): |
| 191 """Return the first line number of function funcname.""" |
| 192 return self.function_lineno.get(funcname) |
| 193 |
| 194 def get_actual_bp(self, lineno, lines=None): |
| 195 """Return the actual breakpoint as (func_lno, actual_lno). |
| 196 |
| 197 func_lno: first line of the corresponding function definition or zero |
| 198 when the breakpoint would be set outside a function. |
| 199 actual_lno: line where the debugger would stop. |
| 200 """ |
| 201 if (not lineno or lineno < 0 or |
| 202 not self.last_line or lineno > self.last_line): |
| 203 return None, None |
| 204 |
| 205 if lines is None: |
| 206 lines = self.functions_lines |
| 207 i = 0 |
| 208 length = j = len(lines) |
| 209 # Find the index of the first lineset whose start is the first to be |
| 210 # strictly greater than lineno. |
| 211 while i < j: |
| 212 m = (i + j) // 2 |
| 213 if lineno < lines[m][0]: |
| 214 j = m |
| 215 else: |
| 216 i = m + 1 |
| 217 |
| 218 if i != 0: |
| 219 ltype = lines[i-1][2] |
| 220 # lineno inside previous LINE_GROUP or LINE_MULTI |
| 221 if lineno <= lines[i-1][1]: |
| 222 if ltype == LineSet.LINE_GROUP: |
| 223 return lines[i-1][3], lineno |
| 224 elif ltype == LineSet.LINE_MULTI: |
| 225 return lines[i-1][3], lines[i-1][0] |
| 226 if lines is self.functions_lines: |
| 227 if i != length: |
| 228 ltype = lines[i][2] |
| 229 # First statement of next group if not LINE_DEF |
| 230 if ltype != LineSet.LINE_DEF: |
| 231 return lines[i][3], lines[i][0] |
| 232 # Outside a function definition |
| 233 return self.get_actual_bp(lineno, self.module_lines) |
| 234 |
| 235 # A comment line, string statement line or empty line outside a |
| 236 # function definition. |
| 237 return None, lineno |
| 238 |
| 239 def _parse(self, tok_generator, lineset=LineSet(), cindent=0, clss=None): |
| 240 func_lno = 0 |
| 241 prev_tokentype = token.ENDMARKER |
| 242 try: |
| 243 for tokentype, tok, srowcol, _end, _line in tok_generator: |
| 244 if tokentype == token.DEDENT: |
| 245 # End of function definition |
| 246 if func_lno and srowcol[1] <= indent: |
| 247 lineset.add(self) |
| 248 func_lno = 0 |
| 249 # End of class definition |
| 250 if clss and srowcol[1] <= cindent: |
| 251 return |
| 252 elif tokentype == tokenize.NL: |
| 253 if lineset.start and lineset.ltype != lineset.LINE_DEF: |
| 254 lineset.add(self) |
| 255 # Start of a logical line with multiple physical lines |
| 256 if (lineset.ltype != lineset.LINE_MULTI and |
| 257 prev_tokentype != tokenize.NEWLINE and |
| 258 prev_tokentype != tokenize.COMMENT): |
| 259 lineset.reset(srowcol[0], lineset.LINE_MULTI, |
| 260 func_lno) |
| 261 elif tokentype == tokenize.NEWLINE: |
| 262 lineset.last = srowcol[0] |
| 263 if lineset.start: |
| 264 if (lineset.ltype == lineset.LINE_DEF or |
| 265 lineset.ltype == lineset.LINE_MULTI): |
| 266 lineset.add(self) |
| 267 elif tokentype == token.ENDMARKER: |
| 268 lineset.add(self) |
| 269 self.last_line = srowcol[0] - 1 |
| 270 elif tok == 'def' or tok == 'class': |
| 271 tokentype, name = next(tok_generator)[0:2] |
| 272 if tokentype != token.NAME: |
| 273 continue # syntax error |
| 274 # Nested def or class in a function |
| 275 if func_lno: |
| 276 self.firstlineno[srowcol[0]] = func_lno |
| 277 prev_tokentype = tokentype |
| 278 continue |
| 279 if clss: |
| 280 name = '{}.{}'.format(clss, name) |
| 281 lineno, indent = srowcol |
| 282 if tok == 'def': |
| 283 lineset.add(self) |
| 284 func_lno = lineno |
| 285 # Start of a function definition |
| 286 lineset.reset(lineno, lineset.LINE_DEF, func_lno) |
| 287 self.function_lineno[name] = lineno |
| 288 self.firstlineno[lineno] = lineno |
| 289 else: |
| 290 self.firstlineno[lineno] = 0 |
| 291 self._parse(tok_generator, lineset, indent, name) |
| 292 elif (not lineset.start and tokentype != tokenize.COMMENT and |
| 293 tokentype != token.INDENT and |
| 294 tokentype != token.STRING): |
| 295 # Start of a group of lines (possibly LINE_MULTI) |
| 296 lineset.reset(srowcol[0], lineset.LINE_GROUP, func_lno) |
| 297 |
| 298 prev_tokentype = tokentype |
| 299 except StopIteration: |
| 300 pass |
| 301 |
| 302 def parse(self): |
| 303 self.function_lineno = {} |
| 304 self.firstlineno = {} |
| 305 self.last_line = 0 |
| 306 self.module_lines = [] |
| 307 self.functions_lines = [] |
| 308 |
| 309 source_lines = linecache.getlines(self.filename) |
| 310 if source_lines: |
| 311 self._parse(tokenize.generate_tokens(iter(source_lines).__next__)) |
| 312 |
| 313 def __str__(self): |
| 314 def lines_per_func(lines): |
| 315 # Group lines per function |
| 316 func_lno = 0 |
| 317 group = [] |
| 318 for lineset in lines: |
| 319 if lineset[3] != func_lno: |
| 320 if group: |
| 321 yield pprint.pformat(group) |
| 322 func_lno = lineset[3] |
| 323 group = [lineset] |
| 324 else: |
| 325 group.append(lineset) |
| 326 if group: |
| 327 yield pprint.pformat(group) |
| 328 |
| 329 return ( |
| 330 'Functions:\n{}\n' |
| 331 'Functions and classes firstlineno:\n{}\n' |
| 332 'Last line: {}\n' |
| 333 'Module lines:\n{}\n' |
| 334 'Functions lines:\n{}\n'.format( |
| 335 pprint.pformat( |
| 336 sorted(self.function_lineno.items(), key=itemgetter(1))), |
| 337 pprint.pformat( |
| 338 sorted(self.firstlineno.items(), key=itemgetter(0))), |
| 339 self.last_line, |
| 340 pprint.pformat(sorted(self.module_lines, key=itemgetter(0))), |
| 341 '\n'.join(lines_per_func(self.functions_lines)))) |
| 342 |
| 343 class ModuleBreakpoints: |
| 344 """The breakpoints of a module. |
| 345 |
| 346 The breakpts attribute is a dictionary that maps the first line of a |
| 347 function definition (or zero for all the lines outside a function |
| 348 definition) to a line_bps dictionary that maps each line of the function, |
| 349 where one or more breakpoints are set, to the list of corresponding |
| 350 Breakpoint instances. |
| 351 |
| 352 Note: |
| 353 A line in line_bps is the actual line of the breakpoint (the line where the |
| 354 debugger stops), this line may differ from the line attribute of the |
| 355 Breakpoint instance as set by the user. |
| 356 """ |
| 357 |
| 358 def __init__(self, filename): |
| 359 assert filename, ('Attempt to instantiate ModuleBreakpoints' |
| 360 ' with {}'.format(filename)) |
| 361 if filename not in _modules: |
| 362 _modules[filename] = ModuleSource(filename) |
| 363 self.module_src = _modules[filename] |
| 364 self.stat = self.module_src.stat |
| 365 self.breakpts = {} |
| 366 |
| 367 def reset(self): |
| 368 self.module_src.reset() |
| 369 stat = self.module_src.stat |
| 370 if not self.stat or not stat: |
| 371 return |
| 372 if (self.stat.st_size == stat.st_size and |
| 373 self.stat.st_mtime == stat.st_mtime): |
| 374 return |
| 375 |
| 376 # The file has been modified |
| 377 self.stat = stat |
| 378 bplist = self.all_breakpoints() |
| 379 self.breakpts = {} |
| 380 for bp in bplist: |
| 381 self.add_breakpoint(bp) |
| 382 |
| 383 def add_breakpoint(self, bp): |
| 384 func_lno, actual_lno = self.module_src.get_actual_bp(bp.line) |
| 385 # May happen after the file has been modified and a reset. |
| 386 if func_lno is None or not actual_lno: |
| 387 return |
| 388 if func_lno not in self.breakpts: |
| 389 self.breakpts[func_lno] = {} |
| 390 line_bps = self.breakpts[func_lno] |
| 391 if actual_lno not in line_bps: |
| 392 line_bps[actual_lno] = [] |
| 393 line_bps[actual_lno].append(bp) |
| 394 |
| 395 def delete_breakpoint(self, bp): |
| 396 func_lno, actual_lno = self.module_src.get_actual_bp(bp.line) |
| 397 # May happen after the file has been modified and a reset. |
| 398 if func_lno is None or not actual_lno: |
| 399 return |
| 400 try: |
| 401 line_bps = self.breakpts[func_lno] |
| 402 bplist = line_bps[actual_lno] |
| 403 bplist.remove(bp) |
| 404 except (KeyError, ValueError): |
| 405 assert False, ('Internal error: bpbynumber and breakpts' |
| 406 ' are inconsistent') |
| 407 if not bplist: |
| 408 del line_bps[actual_lno] |
| 409 if not line_bps: |
| 410 del self.breakpts[func_lno] |
| 411 |
| 412 def get_breakpoints(self, lineno): |
| 413 """Return the list of breakpoints set at lineno.""" |
| 414 func_lno, actual_lno = self.module_src.get_actual_bp(lineno) |
| 415 if func_lno not in self.breakpts: |
| 416 return [] |
| 417 line_bps = self.breakpts[func_lno] |
| 418 if actual_lno not in line_bps: |
| 419 return [] |
| 420 return [bp for bp in sorted(line_bps[actual_lno], |
| 421 key=attrgetter('number')) if bp.line == lineno] |
| 422 |
| 423 def all_breakpoints(self): |
| 424 bpts = [] |
| 425 for line_bps in self.breakpts.values(): |
| 426 for bplist in line_bps.values(): |
| 427 bpts.extend(bplist) |
| 428 return [bp for bp in sorted(bpts, key=attrgetter('number'))] |
| 429 |
| 430 class FunctionQualifiedName: |
| 431 """Utility to find where is defined a function from its qualified name.""" |
| 432 |
| 433 def __init__(self, name, module=None): |
| 434 self.name = name |
| 435 self.cur_fname = None |
| 436 if module: |
| 437 self.cur_fname = getfilename(module.__name__) |
| 438 self.fileline = None, None |
| 439 |
| 440 def get_fileline(self): |
| 441 i = self.name.rfind('.') |
| 442 if i >= 0: |
| 443 funcname = self.name[i+1:] |
| 444 prefix = self.name[:i] |
| 445 j = prefix.rfind('.') |
| 446 # Try first the current module for a class method, then a function |
| 447 # in prefix and last a method in prefix[:j]. |
| 448 if (not (prefix and j < 0 and |
| 449 self.lookup(self.cur_fname, self.name)) and |
| 450 not self.lookup(getfilename(prefix), funcname) and |
| 451 j > 0): |
| 452 method = '{}.{}'.format(prefix[j+1:], funcname) |
| 453 prefix = prefix[:j] |
| 454 self.lookup(getfilename(prefix), method) |
| 455 else: |
| 456 # a function in the current module |
| 457 self.lookup(self.cur_fname, self.name) |
| 458 |
| 459 return self.fileline |
| 460 |
| 461 def lookup(self, filename, funcname): |
| 462 if filename: |
| 463 try: |
| 464 self.fileline = funcname_breakpoint(funcname, filename) |
| 465 return True |
| 466 except BdbError: |
| 467 pass |
| 468 return False |
| 12 | 469 |
| 13 class Bdb: | 470 class Bdb: |
| 14 """Generic Python debugger base class. | 471 """Generic Python debugger base class. |
| 15 | 472 |
| 16 This class takes care of details of the trace facility; | 473 This class takes care of details of the trace facility; |
| 17 a derived class should implement user interaction. | 474 a derived class should implement user interaction. |
| 18 The standard debugger class (pdb.Pdb) is an example. | 475 The standard debugger class (pdb.Pdb) is an example. |
| 19 """ | 476 """ |
| 20 | 477 |
| 21 def __init__(self, skip=None): | 478 def __init__(self, skip=None): |
| 22 self.skip = set(skip) if skip else None | 479 self.skip = set(skip) if skip else None |
| 23 self.breaks = {} | |
| 24 self.fncache = {} | |
| 25 self.frame_returning = None | 480 self.frame_returning = None |
| 481 # A dictionary mapping a filename to a ModuleBreakpoints instance. |
| 482 self.breakpoints = {} |
| 26 | 483 |
| 484 # Backward compatibility |
| 27 def canonic(self, filename): | 485 def canonic(self, filename): |
| 28 if filename == "<" + filename[1:-1] + ">": | 486 return canonic(filename) |
| 29 return filename | |
| 30 canonic = self.fncache.get(filename) | |
| 31 if not canonic: | |
| 32 canonic = os.path.abspath(filename) | |
| 33 canonic = os.path.normcase(canonic) | |
| 34 self.fncache[filename] = canonic | |
| 35 return canonic | |
| 36 | 487 |
| 37 def reset(self): | 488 def reset(self): |
| 38 import linecache | 489 # A dictionary mapping a code object to the first line of the enclosing |
| 490 # non-nested function. |
| 491 self.code_firstlineno = {} |
| 39 linecache.checkcache() | 492 linecache.checkcache() |
| 40 self.botframe = None | 493 self.botframe = None |
| 41 self._set_stopinfo(None, None) | 494 self._set_stopinfo(None, None) |
| 495 for module_src in _modules.values(): |
| 496 module_src.reset() |
| 497 for module_bpts in self.breakpoints.values(): |
| 498 module_bpts.reset() |
| 499 |
| 500 def firstlineno(self, frame): |
| 501 """Return the first line of the enclosing non-nested function.""" |
| 502 lineno = frame.f_code.co_firstlineno |
| 503 if lineno == 1 and frame.f_code.co_name == '<module>': |
| 504 return 0 |
| 505 filename = canonic(frame.f_code.co_filename) |
| 506 if filename not in _modules: |
| 507 _modules[filename] = ModuleSource(filename) |
| 508 module_src = _modules[filename] |
| 509 return module_src.firstlineno.get(lineno, lineno) |
| 510 |
| 511 def _set_local_trace(self, frame): |
| 512 frame.f_trace = self.trace_dispatch |
| 513 if frame.f_code not in self.code_firstlineno: |
| 514 self.code_firstlineno[frame.f_code] = self.firstlineno(frame) |
| 42 | 515 |
| 43 def trace_dispatch(self, frame, event, arg): | 516 def trace_dispatch(self, frame, event, arg): |
| 44 if self.quitting: | 517 if self.quitting: |
| 45 return # None | 518 return # None |
| 46 if event == 'line': | 519 if event == 'line': |
| 47 return self.dispatch_line(frame) | 520 return self.dispatch_line(frame) |
| 48 if event == 'call': | 521 if event == 'call': |
| 49 return self.dispatch_call(frame, arg) | 522 return self.dispatch_call(frame, arg) |
| 50 if event == 'return': | 523 if event == 'return': |
| 51 return self.dispatch_return(frame, arg) | 524 return self.dispatch_return(frame, arg) |
| 52 if event == 'exception': | 525 if event == 'exception': |
| 53 return self.dispatch_exception(frame, arg) | 526 return self.dispatch_exception(frame, arg) |
| 54 if event == 'c_call': | 527 if event == 'c_call': |
| 55 return self.trace_dispatch | 528 return self.trace_dispatch |
| 56 if event == 'c_exception': | 529 if event == 'c_exception': |
| 57 return self.trace_dispatch | 530 return self.trace_dispatch |
| 58 if event == 'c_return': | 531 if event == 'c_return': |
| 59 return self.trace_dispatch | 532 return self.trace_dispatch |
| 60 print('bdb.Bdb.dispatch: unknown debugging event:', repr(event)) | 533 print('bdb.Bdb.dispatch: unknown debugging event:', repr(event)) |
| 61 return self.trace_dispatch | 534 return self.trace_dispatch |
| 62 | 535 |
| 63 def dispatch_line(self, frame): | 536 def dispatch_line(self, frame): |
| 64 if self.stop_here(frame) or self.break_here(frame): | 537 if self.stop_here(frame) or self.break_here(frame): |
| 65 self.user_line(frame) | 538 self.user_line(frame) |
| 66 if self.quitting: raise BdbQuit | 539 if self.quitting: raise BdbQuit |
| 67 return self.trace_dispatch | 540 return self.trace_dispatch |
| 68 | 541 |
| 69 def dispatch_call(self, frame, arg): | 542 def dispatch_call(self, frame, arg): |
| 70 # XXX 'arg' is no longer used | 543 # XXX 'arg' is no longer used |
| 544 if frame.f_code not in self.code_firstlineno: |
| 545 self.code_firstlineno[frame.f_code] = self.firstlineno(frame) |
| 71 if self.botframe is None: | 546 if self.botframe is None: |
| 72 # First call of dispatch since reset() | 547 # First call of dispatch since reset() |
| 73 self.botframe = frame.f_back # (CT) Note that this may also be None! | 548 self.botframe = frame.f_back # (CT) Note that this may also be None! |
| 74 return self.trace_dispatch | 549 return self.trace_dispatch |
| 75 if not (self.stop_here(frame) or self.break_anywhere(frame)): | 550 if not (self.stop_here(frame) or self.break_at_function(frame)): |
| 76 # No need to trace this function | 551 # No need to trace this function |
| 77 return # None | 552 return # None |
| 78 self.user_call(frame, arg) | 553 self.user_call(frame, arg) |
| 79 if self.quitting: raise BdbQuit | 554 if self.quitting: raise BdbQuit |
| 80 return self.trace_dispatch | 555 return self.trace_dispatch |
| 81 | 556 |
| 82 def dispatch_return(self, frame, arg): | 557 def dispatch_return(self, frame, arg): |
| 83 if self.stop_here(frame) or frame == self.returnframe: | 558 if self.stop_here(frame) or frame == self.returnframe: |
| 84 try: | 559 try: |
| 85 self.frame_returning = frame | 560 self.frame_returning = frame |
| (...skipping 29 matching lines...) Expand all Loading... |
| 115 if self.stoplineno == -1: | 590 if self.stoplineno == -1: |
| 116 return False | 591 return False |
| 117 return frame.f_lineno >= self.stoplineno | 592 return frame.f_lineno >= self.stoplineno |
| 118 while frame is not None and frame is not self.stopframe: | 593 while frame is not None and frame is not self.stopframe: |
| 119 if frame is self.botframe: | 594 if frame is self.botframe: |
| 120 return True | 595 return True |
| 121 frame = frame.f_back | 596 frame = frame.f_back |
| 122 return False | 597 return False |
| 123 | 598 |
| 124 def break_here(self, frame): | 599 def break_here(self, frame): |
| 125 filename = self.canonic(frame.f_code.co_filename) | 600 filename = canonic(frame.f_code.co_filename) |
| 126 if filename not in self.breaks: | 601 if filename not in self.breakpoints: |
| 127 return False | 602 return False |
| 128 lineno = frame.f_lineno | 603 func_lno = self.code_firstlineno[frame.f_code] |
| 129 if lineno not in self.breaks[filename]: | 604 module_bpts = self.breakpoints[filename] |
| 130 # The line itself has no breakpoint, but maybe the line is the | 605 if (func_lno not in module_bpts.breakpts or |
| 131 # first line of a function with breakpoint set by function name. | 606 frame.f_lineno not in module_bpts.breakpts[func_lno]): |
| 132 lineno = frame.f_code.co_firstlineno | 607 return False |
| 133 if lineno not in self.breaks[filename]: | |
| 134 return False | |
| 135 | 608 |
| 136 # flag says ok to delete temp. bp | 609 # Handle multiple breakpoints on the same line (issue 14789) |
| 137 (bp, flag) = effective(filename, lineno, frame) | 610 self.effective_bp_list = [] |
| 138 if bp: | 611 for bp in module_bpts.breakpts[func_lno][frame.f_lineno]: |
| 139 self.currentbp = bp.number | 612 stop, delete = bp.process_hit_event(frame) |
| 140 if (flag and bp.temporary): | 613 if stop: |
| 141 self.do_clear(str(bp.number)) | 614 self.effective_bp_list.append(bp.number) |
| 142 return True | 615 if bp.temporary and delete: |
| 143 else: | 616 self.do_clear(str(bp.number)) |
| 144 return False | 617 return len(self.effective_bp_list) != 0 |
| 145 | 618 |
| 146 def do_clear(self, arg): | 619 def do_clear(self, arg): |
| 147 raise NotImplementedError("subclass of bdb must implement do_clear()") | 620 raise NotImplementedError("subclass of bdb must implement do_clear()") |
| 148 | 621 |
| 149 def break_anywhere(self, frame): | 622 def break_at_function(self, frame): |
| 150 return self.canonic(frame.f_code.co_filename) in self.breaks | 623 filename = canonic(frame.f_code.co_filename) |
| 624 return (filename in self.breakpoints and |
| 625 self.code_firstlineno[frame.f_code] in |
| 626 self.breakpoints[filename].breakpts) |
| 151 | 627 |
| 152 # Derived classes should override the user_* methods | 628 # Derived classes should override the user_* methods |
| 153 # to gain control. | 629 # to gain control. |
| 154 | 630 |
| 155 def user_call(self, frame, argument_list): | 631 def user_call(self, frame, argument_list): |
| 156 """This method is called when there is the remote possibility | 632 """This method is called when there is the remote possibility |
| 157 that we ever need to stop in this function.""" | 633 that we ever need to stop in this function.""" |
| 158 pass | 634 pass |
| 159 | 635 |
| 160 def user_line(self, frame): | 636 def user_line(self, frame): |
| (...skipping 30 matching lines...) Expand all Loading... |
| 191 | 667 |
| 192 def set_step(self): | 668 def set_step(self): |
| 193 """Stop after one line of code.""" | 669 """Stop after one line of code.""" |
| 194 # Issue #13183: pdb skips frames after hitting a breakpoint and running | 670 # Issue #13183: pdb skips frames after hitting a breakpoint and running |
| 195 # step commands. | 671 # step commands. |
| 196 # Restore the trace function in the caller (that may not have been set | 672 # Restore the trace function in the caller (that may not have been set |
| 197 # for performance reasons) when returning from the current frame. | 673 # for performance reasons) when returning from the current frame. |
| 198 if self.frame_returning: | 674 if self.frame_returning: |
| 199 caller_frame = self.frame_returning.f_back | 675 caller_frame = self.frame_returning.f_back |
| 200 if caller_frame and not caller_frame.f_trace: | 676 if caller_frame and not caller_frame.f_trace: |
| 201 caller_frame.f_trace = self.trace_dispatch | 677 self._set_local_trace(caller_frame) |
| 202 self._set_stopinfo(None, None) | 678 self._set_stopinfo(None, None) |
| 203 | 679 |
| 204 def set_next(self, frame): | 680 def set_next(self, frame): |
| 205 """Stop on the next line in or below the given frame.""" | 681 """Stop on the next line in or below the given frame.""" |
| 206 self._set_stopinfo(frame, None) | 682 self._set_stopinfo(frame, None) |
| 207 | 683 |
| 208 def set_return(self, frame): | 684 def set_return(self, frame): |
| 209 """Stop when returning from the given frame.""" | 685 """Stop when returning from the given frame.""" |
| 210 self._set_stopinfo(frame.f_back, frame) | 686 self._set_stopinfo(frame.f_back, frame) |
| 211 | 687 |
| 212 def set_trace(self, frame=None): | 688 def set_trace(self, frame=None): |
| 213 """Start debugging from `frame`. | 689 """Start debugging from `frame`. |
| 214 | 690 |
| 215 If frame is not specified, debugging starts from caller's frame. | 691 If frame is not specified, debugging starts from caller's frame. |
| 216 """ | 692 """ |
| 217 if frame is None: | 693 if frame is None: |
| 218 frame = sys._getframe().f_back | 694 frame = sys._getframe().f_back |
| 219 self.reset() | 695 self.reset() |
| 220 while frame: | 696 while frame: |
| 221 frame.f_trace = self.trace_dispatch | 697 self._set_local_trace(frame) |
| 222 self.botframe = frame | 698 self.botframe = frame |
| 223 frame = frame.f_back | 699 frame = frame.f_back |
| 224 self.set_step() | 700 self.set_step() |
| 225 sys.settrace(self.trace_dispatch) | 701 sys.settrace(self.trace_dispatch) |
| 226 | 702 |
| 227 def set_continue(self): | 703 def set_continue(self): |
| 228 # Don't stop except at breakpoints or when finished | 704 # Don't stop except at breakpoints or when finished |
| 229 self._set_stopinfo(self.botframe, None, -1) | 705 self._set_stopinfo(self.botframe, None, -1) |
| 230 if not self.breaks: | 706 if not self.has_breaks(): |
| 231 # no breakpoints; run without debugger overhead | 707 # no breakpoints; run without debugger overhead |
| 232 sys.settrace(None) | 708 sys.settrace(None) |
| 233 frame = sys._getframe().f_back | 709 frame = sys._getframe().f_back |
| 234 while frame and frame is not self.botframe: | 710 while frame and frame is not self.botframe: |
| 235 del frame.f_trace | 711 del frame.f_trace |
| 236 frame = frame.f_back | 712 frame = frame.f_back |
| 237 | 713 |
| 238 def set_quit(self): | 714 def set_quit(self): |
| 239 self.stopframe = self.botframe | 715 self.stopframe = self.botframe |
| 240 self.returnframe = None | 716 self.returnframe = None |
| 241 self.quitting = True | 717 self.quitting = True |
| 242 sys.settrace(None) | 718 sys.settrace(None) |
| 243 | 719 |
| 244 # Derived classes and clients can call the following methods | 720 # Derived classes and clients can call the following methods |
| 245 # to manipulate breakpoints. These methods return an | 721 # to manipulate breakpoints. These methods return an |
| 246 # error message is something went wrong, None if all is well. | 722 # error message is something went wrong, None if all is well. |
| 247 # Set_break prints out the breakpoint line and file:lineno. | |
| 248 # Call self.get_*break*() to see the breakpoints or better | 723 # Call self.get_*break*() to see the breakpoints or better |
| 249 # for bp in Breakpoint.bpbynumber: if bp: bp.bpprint(). | 724 # for bp in Breakpoint.bpbynumber: if bp: bp.bpprint(). |
| 250 | 725 |
| 251 def set_break(self, filename, lineno, temporary=False, cond=None, | 726 def set_break(self, fname, lineno, temporary=False, cond=None, |
| 252 funcname=None): | 727 funcname=None): |
| 253 filename = self.canonic(filename) | 728 # funcname is not used anymore and kept for backward compatibility |
| 254 import linecache # Import as late as possible | 729 filename = canonic(fname) |
| 255 line = linecache.getline(filename, lineno) | 730 if filename not in self.breakpoints: |
| 256 if not line: | 731 module_bps = ModuleBreakpoints(filename) |
| 257 return 'Line %s:%d does not exist' % (filename, lineno) | 732 module_src = module_bps.module_src |
| 258 list = self.breaks.setdefault(filename, []) | 733 if not module_src.functions_lines and not module_src.module_lines: |
| 259 if lineno not in list: | 734 return 'No lines in {}'.format(fname) |
| 260 list.append(lineno) | 735 self.breakpoints[filename] = module_bps |
| 261 bp = Breakpoint(filename, lineno, temporary, cond, funcname) | 736 else: |
| 262 | 737 module_bps = self.breakpoints[filename] |
| 263 def _prune_breaks(self, filename, lineno): | 738 func_lno, actual_lno = module_bps.module_src.get_actual_bp(lineno) |
| 264 if (filename, lineno) not in Breakpoint.bplist: | 739 if func_lno is None: |
| 265 self.breaks[filename].remove(lineno) | 740 if not actual_lno: |
| 266 if not self.breaks[filename]: | 741 return 'Line {}:{} does not exist'.format(fname, lineno) |
| 267 del self.breaks[filename] | 742 return ('A comment line, string statement line or empty line' |
| 743 ' outside a function definition {}:{}' |
| 744 .format(fname, lineno)) |
| 745 bp = Breakpoint(filename, lineno, module_bps, temporary, cond) |
| 268 | 746 |
| 269 def clear_break(self, filename, lineno): | 747 def clear_break(self, filename, lineno): |
| 270 filename = self.canonic(filename) | 748 bplist = self.get_breaks(filename, lineno) |
| 271 if filename not in self.breaks: | 749 if not bplist: |
| 272 return 'There are no breakpoints in %s' % filename | |
| 273 if lineno not in self.breaks[filename]: | |
| 274 return 'There is no breakpoint at %s:%d' % (filename, lineno) | 750 return 'There is no breakpoint at %s:%d' % (filename, lineno) |
| 275 # If there's only one bp in the list for that file,line | 751 for bp in bplist: |
| 276 # pair, then remove the breaks entry | |
| 277 for bp in Breakpoint.bplist[filename, lineno][:]: | |
| 278 bp.deleteMe() | 752 bp.deleteMe() |
| 279 self._prune_breaks(filename, lineno) | |
| 280 | 753 |
| 281 def clear_bpbynumber(self, arg): | 754 def clear_bpbynumber(self, arg): |
| 282 try: | 755 try: |
| 283 bp = self.get_bpbynumber(arg) | 756 bp = self.get_bpbynumber(arg) |
| 284 except ValueError as err: | 757 except ValueError as err: |
| 285 return str(err) | 758 return str(err) |
| 286 bp.deleteMe() | 759 bp.deleteMe() |
| 287 self._prune_breaks(bp.file, bp.line) | |
| 288 | 760 |
| 289 def clear_all_file_breaks(self, filename): | 761 def clear_all_file_breaks(self, fname): |
| 290 filename = self.canonic(filename) | 762 filename = canonic(fname) |
| 291 if filename not in self.breaks: | 763 if (filename not in self.breakpoints or not |
| 292 return 'There are no breakpoints in %s' % filename | 764 self.breakpoints[filename].breakpts.keys()): |
| 293 for line in self.breaks[filename]: | 765 return 'There are no breakpoints in %s' % fname |
| 294 blist = Breakpoint.bplist[filename, line] | 766 for bp in self.breakpoints[filename].all_breakpoints(): |
| 295 for bp in blist: | 767 bp.deleteMe() |
| 296 bp.deleteMe() | |
| 297 del self.breaks[filename] | |
| 298 | 768 |
| 299 def clear_all_breaks(self): | 769 def clear_all_breaks(self): |
| 300 if not self.breaks: | 770 if not self.has_breaks(): |
| 301 return 'There are no breakpoints' | 771 return 'There are no breakpoints' |
| 302 for bp in Breakpoint.bpbynumber: | 772 for bp in Breakpoint.bpbynumber: |
| 303 if bp: | 773 if bp: |
| 304 bp.deleteMe() | 774 bp.deleteMe() |
| 305 self.breaks = {} | |
| 306 | 775 |
| 307 def get_bpbynumber(self, arg): | 776 def get_bpbynumber(self, arg): |
| 308 if not arg: | 777 if not arg: |
| 309 raise ValueError('Breakpoint number expected') | 778 raise ValueError('Breakpoint number expected') |
| 310 try: | 779 try: |
| 311 number = int(arg) | 780 number = int(arg) |
| 312 except ValueError: | 781 except ValueError: |
| 313 raise ValueError('Non-numeric breakpoint number %s' % arg) | 782 raise ValueError('Non-numeric breakpoint number %s' % arg) |
| 314 try: | 783 try: |
| 315 bp = Breakpoint.bpbynumber[number] | 784 bp = Breakpoint.bpbynumber[number] |
| 316 except IndexError: | 785 except IndexError: |
| 317 raise ValueError('Breakpoint number %d out of range' % number) | 786 raise ValueError('Breakpoint number %d out of range' % number) |
| 318 if bp is None: | 787 if bp is None: |
| 319 raise ValueError('Breakpoint %d already deleted' % number) | 788 raise ValueError('Breakpoint %d already deleted' % number) |
| 320 return bp | 789 return bp |
| 321 | 790 |
| 322 def get_break(self, filename, lineno): | 791 def get_break(self, filename, lineno): |
| 323 filename = self.canonic(filename) | 792 return len(self.get_breaks(filename, lineno)) != 0 |
| 324 return filename in self.breaks and \ | |
| 325 lineno in self.breaks[filename] | |
| 326 | 793 |
| 327 def get_breaks(self, filename, lineno): | 794 def get_breaks(self, filename, lineno): |
| 328 filename = self.canonic(filename) | 795 filename = canonic(filename) |
| 329 return filename in self.breaks and \ | 796 if filename in self.breakpoints: |
| 330 lineno in self.breaks[filename] and \ | 797 return self.breakpoints[filename].get_breakpoints(lineno) |
| 331 Breakpoint.bplist[filename, lineno] or [] | 798 return [] |
| 332 | 799 |
| 333 def get_file_breaks(self, filename): | 800 def get_file_breaks(self, filename): |
| 334 filename = self.canonic(filename) | 801 filename = canonic(filename) |
| 335 if filename in self.breaks: | 802 if filename not in self.breakpoints: |
| 336 return self.breaks[filename] | |
| 337 else: | |
| 338 return [] | 803 return [] |
| 804 return [bp.line for bp in self.breakpoints[filename].all_breakpoints()] |
| 339 | 805 |
| 340 def get_all_breaks(self): | 806 def get_all_breaks(self): |
| 341 return self.breaks | 807 breaks = {} |
| 808 for filename in self.breakpoints: |
| 809 linebp_list = self.get_file_breaks(filename) |
| 810 if linebp_list: |
| 811 breaks[filename] = self.get_file_breaks(filename) |
| 812 return breaks |
| 813 |
| 814 def has_breaks(self): |
| 815 return any(self.breakpoints[f].breakpts.keys() |
| 816 for f in self.breakpoints) |
| 342 | 817 |
| 343 # Derived classes and clients can call the following method | 818 # Derived classes and clients can call the following method |
| 344 # to get a data structure representing a stack trace. | 819 # to get a data structure representing a stack trace. |
| 345 | 820 |
| 346 def get_stack(self, f, t): | 821 def get_stack(self, f, t): |
| 347 stack = [] | 822 stack = [] |
| 348 if t and t.tb_frame is f: | 823 if t and t.tb_frame is f: |
| 349 t = t.tb_next | 824 t = t.tb_next |
| 350 while f is not None: | 825 while f is not None: |
| 351 stack.append((f, f.f_lineno)) | 826 stack.append((f, f.f_lineno)) |
| 352 if f is self.botframe: | 827 if f is self.botframe: |
| 353 break | 828 break |
| 354 f = f.f_back | 829 f = f.f_back |
| 355 stack.reverse() | 830 stack.reverse() |
| 356 i = max(0, len(stack) - 1) | 831 i = max(0, len(stack) - 1) |
| 357 while t is not None: | 832 while t is not None: |
| 358 stack.append((t.tb_frame, t.tb_lineno)) | 833 stack.append((t.tb_frame, t.tb_lineno)) |
| 359 t = t.tb_next | 834 t = t.tb_next |
| 360 if f is None: | 835 if f is None: |
| 361 i = max(0, len(stack) - 1) | 836 i = max(0, len(stack) - 1) |
| 362 return stack, i | 837 return stack, i |
| 363 | 838 |
| 364 def format_stack_entry(self, frame_lineno, lprefix=': '): | 839 def format_stack_entry(self, frame_lineno, lprefix=': '): |
| 365 import linecache, reprlib | 840 import reprlib |
| 366 frame, lineno = frame_lineno | 841 frame, lineno = frame_lineno |
| 367 filename = self.canonic(frame.f_code.co_filename) | 842 filename = canonic(frame.f_code.co_filename) |
| 368 s = '%s(%r)' % (filename, lineno) | 843 s = '%s(%r)' % (filename, lineno) |
| 369 if frame.f_code.co_name: | 844 if frame.f_code.co_name: |
| 370 s += frame.f_code.co_name | 845 s += frame.f_code.co_name |
| 371 else: | 846 else: |
| 372 s += "<lambda>" | 847 s += "<lambda>" |
| 373 if '__args__' in frame.f_locals: | 848 if '__args__' in frame.f_locals: |
| 374 args = frame.f_locals['__args__'] | 849 args = frame.f_locals['__args__'] |
| 375 else: | 850 else: |
| 376 args = None | 851 args = None |
| 377 if args: | 852 if args: |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 448 def set_trace(): | 923 def set_trace(): |
| 449 Bdb().set_trace() | 924 Bdb().set_trace() |
| 450 | 925 |
| 451 | 926 |
| 452 class Breakpoint: | 927 class Breakpoint: |
| 453 """Breakpoint class. | 928 """Breakpoint class. |
| 454 | 929 |
| 455 Implements temporary breakpoints, ignore counts, disabling and | 930 Implements temporary breakpoints, ignore counts, disabling and |
| 456 (re)-enabling, and conditionals. | 931 (re)-enabling, and conditionals. |
| 457 | 932 |
| 458 Breakpoints are indexed by number through bpbynumber and by | 933 Breakpoints are indexed by number through bpbynumber. |
| 459 the file,line tuple using bplist. The former points to a | |
| 460 single instance of class Breakpoint. The latter points to a | |
| 461 list of such instances since there may be more than one | |
| 462 breakpoint per line. | |
| 463 | 934 |
| 464 """ | 935 """ |
| 465 | 936 |
| 466 # XXX Keeping state in the class is a mistake -- this means | 937 next = 1 # Next bp to be assigned |
| 467 # you cannot have more than one active Bdb instance. | 938 bpbynumber = [None] # Each entry is None or an instance of Bpt |
| 468 | 939 |
| 469 next = 1 # Next bp to be assigned | 940 def __init__(self, file, line, module, temporary=False, |
| 470 bplist = {} # indexed by (file, lineno) tuple | 941 cond=None): |
| 471 bpbynumber = [None] # Each entry is None or an instance of Bpt | |
| 472 # index 0 is unused, except for marking an | |
| 473 # effective break .... see effective() | |
| 474 | |
| 475 def __init__(self, file, line, temporary=False, cond=None, funcname=None): | |
| 476 self.funcname = funcname | |
| 477 # Needed if funcname is not None. | |
| 478 self.func_first_executable_line = None | |
| 479 self.file = file # This better be in canonical form! | 942 self.file = file # This better be in canonical form! |
| 480 self.line = line | 943 self.line = line |
| 944 self.module = module |
| 481 self.temporary = temporary | 945 self.temporary = temporary |
| 482 self.cond = cond | 946 self.cond = cond |
| 483 self.enabled = True | 947 self.enabled = True |
| 484 self.ignore = 0 | 948 self.ignore = 0 |
| 485 self.hits = 0 | 949 self.hits = 0 |
| 486 self.number = Breakpoint.next | 950 self.number = Breakpoint.next |
| 487 Breakpoint.next += 1 | 951 Breakpoint.next += 1 |
| 488 # Build the two lists | |
| 489 self.bpbynumber.append(self) | 952 self.bpbynumber.append(self) |
| 490 if (file, line) in self.bplist: | 953 self.module.add_breakpoint(self) |
| 491 self.bplist[file, line].append(self) | |
| 492 else: | |
| 493 self.bplist[file, line] = [self] | |
| 494 | 954 |
| 495 def deleteMe(self): | 955 def deleteMe(self): |
| 496 index = (self.file, self.line) | 956 if self.bpbynumber[self.number]: |
| 497 self.bpbynumber[self.number] = None # No longer in list | 957 self.bpbynumber[self.number] = None # No longer in list |
| 498 self.bplist[index].remove(self) | 958 self.module.delete_breakpoint(self) |
| 499 if not self.bplist[index]: | |
| 500 # No more bp for this f:l combo | |
| 501 del self.bplist[index] | |
| 502 | 959 |
| 503 def enable(self): | 960 def enable(self): |
| 504 self.enabled = True | 961 self.enabled = True |
| 505 | 962 |
| 506 def disable(self): | 963 def disable(self): |
| 507 self.enabled = False | 964 self.enabled = False |
| 965 |
| 966 def process_hit_event(self, frame): |
| 967 """Return (stop_state, delete_temporary) at a breakpoint hit event.""" |
| 968 if not self.enabled: |
| 969 return False, False |
| 970 # Count every hit when breakpoint is enabled. |
| 971 self.hits += 1 |
| 972 # A conditional breakpoint. |
| 973 if self.cond: |
| 974 try: |
| 975 if not eval(self.cond, frame.f_globals, frame.f_locals): |
| 976 return False, False |
| 977 except: |
| 978 # If the breakpoint condition evaluation fails, the most |
| 979 # conservative thing is to stop on the breakpoint. Don't |
| 980 # delete temporary, as another hint to the user. |
| 981 return True, False |
| 982 if self.ignore > 0: |
| 983 self.ignore -= 1 |
| 984 return False, False |
| 985 return True, True |
| 508 | 986 |
| 509 def bpprint(self, out=None): | 987 def bpprint(self, out=None): |
| 510 if out is None: | 988 if out is None: |
| 511 out = sys.stdout | 989 out = sys.stdout |
| 512 print(self.bpformat(), file=out) | 990 print(self.bpformat(), file=out) |
| 513 | 991 |
| 514 def bpformat(self): | 992 def bpformat(self): |
| 515 if self.temporary: | 993 if self.temporary: |
| 516 disp = 'del ' | 994 disp = 'del ' |
| 517 else: | 995 else: |
| (...skipping 11 matching lines...) Expand all Loading... |
| 529 if self.hits: | 1007 if self.hits: |
| 530 if self.hits > 1: | 1008 if self.hits > 1: |
| 531 ss = 's' | 1009 ss = 's' |
| 532 else: | 1010 else: |
| 533 ss = '' | 1011 ss = '' |
| 534 ret += '\n\tbreakpoint already hit %d time%s' % (self.hits, ss) | 1012 ret += '\n\tbreakpoint already hit %d time%s' % (self.hits, ss) |
| 535 return ret | 1013 return ret |
| 536 | 1014 |
| 537 def __str__(self): | 1015 def __str__(self): |
| 538 return 'breakpoint %s at %s:%s' % (self.number, self.file, self.line) | 1016 return 'breakpoint %s at %s:%s' % (self.number, self.file, self.line) |
| 539 | |
| 540 # -----------end of Breakpoint class---------- | |
| 541 | |
| 542 def checkfuncname(b, frame): | |
| 543 """Check whether we should break here because of `b.funcname`.""" | |
| 544 if not b.funcname: | |
| 545 # Breakpoint was set via line number. | |
| 546 if b.line != frame.f_lineno: | |
| 547 # Breakpoint was set at a line with a def statement and the function | |
| 548 # defined is called: don't break. | |
| 549 return False | |
| 550 return True | |
| 551 | |
| 552 # Breakpoint set via function name. | |
| 553 | |
| 554 if frame.f_code.co_name != b.funcname: | |
| 555 # It's not a function call, but rather execution of def statement. | |
| 556 return False | |
| 557 | |
| 558 # We are in the right frame. | |
| 559 if not b.func_first_executable_line: | |
| 560 # The function is entered for the 1st time. | |
| 561 b.func_first_executable_line = frame.f_lineno | |
| 562 | |
| 563 if b.func_first_executable_line != frame.f_lineno: | |
| 564 # But we are not at the first line number: don't break. | |
| 565 return False | |
| 566 return True | |
| 567 | |
| 568 # Determines if there is an effective (active) breakpoint at this | |
| 569 # line of code. Returns breakpoint number or 0 if none | |
| 570 def effective(file, line, frame): | |
| 571 """Determine which breakpoint for this file:line is to be acted upon. | |
| 572 | |
| 573 Called only if we know there is a bpt at this | |
| 574 location. Returns breakpoint that was triggered and a flag | |
| 575 that indicates if it is ok to delete a temporary bp. | |
| 576 | |
| 577 """ | |
| 578 possibles = Breakpoint.bplist[file, line] | |
| 579 for b in possibles: | |
| 580 if not b.enabled: | |
| 581 continue | |
| 582 if not checkfuncname(b, frame): | |
| 583 continue | |
| 584 # Count every hit when bp is enabled | |
| 585 b.hits += 1 | |
| 586 if not b.cond: | |
| 587 # If unconditional, and ignoring go on to next, else break | |
| 588 if b.ignore > 0: | |
| 589 b.ignore -= 1 | |
| 590 continue | |
| 591 else: | |
| 592 # breakpoint and marker that it's ok to delete if temporary | |
| 593 return (b, True) | |
| 594 else: | |
| 595 # Conditional bp. | |
| 596 # Ignore count applies only to those bpt hits where the | |
| 597 # condition evaluates to true. | |
| 598 try: | |
| 599 val = eval(b.cond, frame.f_globals, frame.f_locals) | |
| 600 if val: | |
| 601 if b.ignore > 0: | |
| 602 b.ignore -= 1 | |
| 603 # continue | |
| 604 else: | |
| 605 return (b, True) | |
| 606 # else: | |
| 607 # continue | |
| 608 except: | |
| 609 # if eval fails, most conservative thing is to stop on | |
| 610 # breakpoint regardless of ignore count. Don't delete | |
| 611 # temporary, as another hint to user. | |
| 612 return (b, False) | |
| 613 return (None, None) | |
| 614 | 1017 |
| 615 | 1018 |
| 616 # -------------------- testing -------------------- | 1019 # -------------------- testing -------------------- |
| 617 | 1020 |
| 618 class Tdb(Bdb): | 1021 class Tdb(Bdb): |
| 619 def user_call(self, frame, args): | 1022 def user_call(self, frame, args): |
| 620 name = frame.f_code.co_name | 1023 name = frame.f_code.co_name |
| 621 if not name: name = '???' | 1024 if not name: name = '???' |
| 622 print('+++ call', name, args) | 1025 print('+++ call', name, args) |
| 623 def user_line(self, frame): | 1026 def user_line(self, frame): |
| 624 import linecache | |
| 625 name = frame.f_code.co_name | 1027 name = frame.f_code.co_name |
| 626 if not name: name = '???' | 1028 if not name: name = '???' |
| 627 fn = self.canonic(frame.f_code.co_filename) | 1029 fn = canonic(frame.f_code.co_filename) |
| 628 line = linecache.getline(fn, frame.f_lineno, frame.f_globals) | 1030 line = linecache.getline(fn, frame.f_lineno, frame.f_globals) |
| 629 print('+++', fn, frame.f_lineno, name, ':', line.strip()) | 1031 print('+++', fn, frame.f_lineno, name, ':', line.strip()) |
| 630 def user_return(self, frame, retval): | 1032 def user_return(self, frame, retval): |
| 631 print('+++ return', retval) | 1033 print('+++ return', retval) |
| 632 def user_exception(self, frame, exc_stuff): | 1034 def user_exception(self, frame, exc_stuff): |
| 633 print('+++ exception', exc_stuff) | 1035 print('+++ exception', exc_stuff) |
| 634 self.set_continue() | 1036 self.set_continue() |
| 635 | 1037 |
| 636 def foo(n): | 1038 def foo(n): |
| 637 print('foo(', n, ')') | 1039 print('foo(', n, ')') |
| 638 x = bar(n*10) | 1040 x = bar(n*10) |
| 639 print('bar returned', x) | 1041 print('bar returned', x) |
| 640 | 1042 |
| 641 def bar(a): | 1043 def bar(a): |
| 642 print('bar(', a, ')') | 1044 print('bar(', a, ')') |
| 643 return a/2 | 1045 return a/2 |
| 644 | 1046 |
| 645 def test(): | 1047 def test(): |
| 646 t = Tdb() | 1048 t = Tdb() |
| 647 t.run('import bdb; bdb.foo(10)') | 1049 t.run('import bdb; bdb.foo(10)') |
| OLD | NEW |