bdb.py (15304B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\carbon\common\stdlib\bdb.py 2 import fnmatch 3 import sys 4 import os 5 import types 6 __all__ = ['BdbQuit', 'Bdb', 'Breakpoint'] 7 8 class BdbQuit(Exception): 9 pass 10 11 12 class Bdb(): 13 14 def __init__(self, skip = None): 15 self.skip = set(skip) if skip else None 16 self.breaks = {} 17 self.fncache = {} 18 19 def canonic(self, filename): 20 if filename == '<' + filename[1:-1] + '>': 21 return filename 22 canonic = self.fncache.get(filename) 23 if not canonic: 24 canonic = os.path.abspath(filename) 25 canonic = os.path.normcase(canonic) 26 self.fncache[filename] = canonic 27 return canonic 28 29 def reset(self): 30 import linecache 31 linecache.checkcache() 32 self.botframe = None 33 self._set_stopinfo(None, None) 34 35 def trace_dispatch(self, frame, event, arg): 36 if self.quitting: 37 return 38 if event == 'line': 39 return self.dispatch_line(frame) 40 if event == 'call': 41 return self.dispatch_call(frame, arg) 42 if event == 'return': 43 return self.dispatch_return(frame, arg) 44 if event == 'exception': 45 return self.dispatch_exception(frame, arg) 46 if event == 'c_call': 47 return self.trace_dispatch 48 if event == 'c_exception': 49 return self.trace_dispatch 50 if event == 'c_return': 51 return self.trace_dispatch 52 print 'bdb.Bdb.dispatch: unknown debugging event:', repr(event) 53 return self.trace_dispatch 54 55 def dispatch_line(self, frame): 56 if self.stop_here(frame) or self.break_here(frame): 57 self.user_line(frame) 58 if self.quitting: 59 raise BdbQuit 60 return self.trace_dispatch 61 62 def dispatch_call(self, frame, arg): 63 if self.botframe is None: 64 self.botframe = frame.f_back 65 return self.trace_dispatch 66 if not (self.stop_here(frame) or self.break_anywhere(frame)): 67 return 68 self.user_call(frame, arg) 69 if self.quitting: 70 raise BdbQuit 71 return self.trace_dispatch 72 73 def dispatch_return(self, frame, arg): 74 if self.stop_here(frame) or frame == self.returnframe: 75 self.user_return(frame, arg) 76 if self.quitting: 77 raise BdbQuit 78 return self.trace_dispatch 79 80 def dispatch_exception(self, frame, arg): 81 if self.stop_here(frame): 82 self.user_exception(frame, arg) 83 if self.quitting: 84 raise BdbQuit 85 return self.trace_dispatch 86 87 def is_skipped_module(self, module_name): 88 for pattern in self.skip: 89 if fnmatch.fnmatch(module_name, pattern): 90 return True 91 92 return False 93 94 def stop_here(self, frame): 95 if self.skip and self.is_skipped_module(frame.f_globals.get('__name__')): 96 return False 97 if frame is self.stopframe: 98 if self.stoplineno == -1: 99 return False 100 return frame.f_lineno >= self.stoplineno 101 while frame is not None and frame is not self.stopframe: 102 if frame is self.botframe: 103 return True 104 frame = frame.f_back 105 106 return False 107 108 def break_here(self, frame): 109 filename = self.canonic(frame.f_code.co_filename) 110 if filename not in self.breaks: 111 return False 112 lineno = frame.f_lineno 113 if lineno not in self.breaks[filename]: 114 lineno = frame.f_code.co_firstlineno 115 if lineno not in self.breaks[filename]: 116 return False 117 bp, flag = effective(filename, lineno, frame) 118 if bp: 119 self.currentbp = bp.number 120 if flag and bp.temporary: 121 self.do_clear(str(bp.number)) 122 return True 123 else: 124 return False 125 126 def do_clear(self, arg): 127 raise NotImplementedError, 'subclass of bdb must implement do_clear()' 128 129 def break_anywhere(self, frame): 130 return self.canonic(frame.f_code.co_filename) in self.breaks 131 132 def user_call(self, frame, argument_list): 133 pass 134 135 def user_line(self, frame): 136 pass 137 138 def user_return(self, frame, return_value): 139 pass 140 141 def user_exception(self, frame, exc_info): 142 exc_type, exc_value, exc_traceback = exc_info 143 144 def _set_stopinfo(self, stopframe, returnframe, stoplineno = 0): 145 self.stopframe = stopframe 146 self.returnframe = returnframe 147 self.quitting = 0 148 self.stoplineno = stoplineno 149 150 def set_until(self, frame): 151 self._set_stopinfo(frame, frame, frame.f_lineno + 1) 152 153 def set_step(self): 154 self._set_stopinfo(None, None) 155 156 def set_next(self, frame): 157 self._set_stopinfo(frame, None) 158 159 def set_return(self, frame): 160 self._set_stopinfo(frame.f_back, frame) 161 162 def set_trace(self, frame = None): 163 if frame is None: 164 frame = sys._getframe().f_back 165 self.reset() 166 while frame: 167 frame.f_trace = self.trace_dispatch 168 self.botframe = frame 169 frame = frame.f_back 170 171 self.set_step() 172 sys.settrace(self.trace_dispatch) 173 174 def set_continue(self): 175 self._set_stopinfo(self.botframe, None, -1) 176 if not self.breaks: 177 sys.settrace(None) 178 frame = sys._getframe().f_back 179 while frame and frame is not self.botframe: 180 del frame.f_trace 181 frame = frame.f_back 182 183 def set_quit(self): 184 self.stopframe = self.botframe 185 self.returnframe = None 186 self.quitting = 1 187 sys.settrace(None) 188 189 def set_break(self, filename, lineno, temporary = 0, cond = None, funcname = None): 190 filename = self.canonic(filename) 191 import linecache 192 line = linecache.getline(filename, lineno) 193 if not line: 194 return 'Line %s:%d does not exist' % (filename, lineno) 195 if filename not in self.breaks: 196 self.breaks[filename] = [] 197 list = self.breaks[filename] 198 if lineno not in list: 199 list.append(lineno) 200 bp = Breakpoint(filename, lineno, temporary, cond, funcname) 201 202 def clear_break(self, filename, lineno): 203 filename = self.canonic(filename) 204 if filename not in self.breaks: 205 return 'There are no breakpoints in %s' % filename 206 if lineno not in self.breaks[filename]: 207 return 'There is no breakpoint at %s:%d' % (filename, lineno) 208 for bp in Breakpoint.bplist[filename, lineno][:]: 209 bp.deleteMe() 210 211 if (filename, lineno) not in Breakpoint.bplist: 212 self.breaks[filename].remove(lineno) 213 if not self.breaks[filename]: 214 del self.breaks[filename] 215 216 def clear_bpbynumber(self, arg): 217 try: 218 number = int(arg) 219 except: 220 return 'Non-numeric breakpoint number (%s)' % arg 221 222 try: 223 bp = Breakpoint.bpbynumber[number] 224 except IndexError: 225 return 'Breakpoint number (%d) out of range' % number 226 227 if not bp: 228 return 'Breakpoint (%d) already deleted' % number 229 self.clear_break(bp.file, bp.line) 230 231 def clear_all_file_breaks(self, filename): 232 filename = self.canonic(filename) 233 if filename not in self.breaks: 234 return 'There are no breakpoints in %s' % filename 235 for line in self.breaks[filename]: 236 blist = Breakpoint.bplist[filename, line] 237 for bp in blist: 238 bp.deleteMe() 239 240 del self.breaks[filename] 241 242 def clear_all_breaks(self): 243 if not self.breaks: 244 return 'There are no breakpoints' 245 for bp in Breakpoint.bpbynumber: 246 if bp: 247 bp.deleteMe() 248 249 self.breaks = {} 250 251 def get_break(self, filename, lineno): 252 filename = self.canonic(filename) 253 return filename in self.breaks and lineno in self.breaks[filename] 254 255 def get_breaks(self, filename, lineno): 256 filename = self.canonic(filename) 257 return filename in self.breaks and lineno in self.breaks[filename] and Breakpoint.bplist[filename, lineno] or [] 258 259 def get_file_breaks(self, filename): 260 filename = self.canonic(filename) 261 if filename in self.breaks: 262 return self.breaks[filename] 263 else: 264 return [] 265 266 def get_all_breaks(self): 267 return self.breaks 268 269 def get_stack(self, f, t): 270 stack = [] 271 if t and t.tb_frame is f: 272 t = t.tb_next 273 while f is not None: 274 stack.append((f, f.f_lineno)) 275 if f is self.botframe: 276 break 277 f = f.f_back 278 279 stack.reverse() 280 i = max(0, len(stack) - 1) 281 while t is not None: 282 stack.append((t.tb_frame, t.tb_lineno)) 283 t = t.tb_next 284 285 if f is None: 286 i = max(0, len(stack) - 1) 287 return (stack, i) 288 289 def format_stack_entry(self, frame_lineno, lprefix = ': '): 290 import linecache, repr 291 frame, lineno = frame_lineno 292 filename = self.canonic(frame.f_code.co_filename) 293 s = '%s(%r)' % (filename, lineno) 294 if frame.f_code.co_name: 295 s = s + frame.f_code.co_name 296 else: 297 s = s + '<lambda>' 298 if '__args__' in frame.f_locals: 299 args = frame.f_locals['__args__'] 300 else: 301 args = None 302 if args: 303 s = s + repr.repr(args) 304 else: 305 s = s + '()' 306 if '__return__' in frame.f_locals: 307 rv = frame.f_locals['__return__'] 308 s = s + '->' 309 s = s + repr.repr(rv) 310 line = linecache.getline(filename, lineno, frame.f_globals) 311 if line: 312 s = s + lprefix + line.strip() 313 return s 314 315 def run(self, cmd, globals = None, locals = None): 316 if globals is None: 317 import __main__ 318 globals = __main__.__dict__ 319 if locals is None: 320 locals = globals 321 self.reset() 322 sys.settrace(self.trace_dispatch) 323 if not isinstance(cmd, types.CodeType): 324 cmd = cmd + '\n' 325 try: 326 exec cmd in globals, locals 327 except BdbQuit: 328 pass 329 finally: 330 self.quitting = 1 331 sys.settrace(None) 332 333 def runeval(self, expr, globals = None, locals = None): 334 if globals is None: 335 import __main__ 336 globals = __main__.__dict__ 337 if locals is None: 338 locals = globals 339 self.reset() 340 sys.settrace(self.trace_dispatch) 341 if not isinstance(expr, types.CodeType): 342 expr = expr + '\n' 343 try: 344 return eval(expr, globals, locals) 345 except BdbQuit: 346 pass 347 finally: 348 self.quitting = 1 349 sys.settrace(None) 350 351 def runctx(self, cmd, globals, locals): 352 self.run(cmd, globals, locals) 353 354 def runcall(self, func, *args, **kwds): 355 self.reset() 356 sys.settrace(self.trace_dispatch) 357 res = None 358 try: 359 res = func(*args, **kwds) 360 except BdbQuit: 361 pass 362 finally: 363 self.quitting = 1 364 sys.settrace(None) 365 366 return res 367 368 369 def set_trace(): 370 Bdb().set_trace() 371 372 373 class Breakpoint(): 374 next = 1 375 bplist = {} 376 bpbynumber = [None] 377 378 def __init__(self, file, line, temporary = 0, cond = None, funcname = None): 379 self.funcname = funcname 380 self.func_first_executable_line = None 381 self.file = file 382 self.line = line 383 self.temporary = temporary 384 self.cond = cond 385 self.enabled = 1 386 self.ignore = 0 387 self.hits = 0 388 self.number = Breakpoint.next 389 Breakpoint.next = Breakpoint.next + 1 390 self.bpbynumber.append(self) 391 if (file, line) in self.bplist: 392 self.bplist[file, line].append(self) 393 else: 394 self.bplist[file, line] = [self] 395 396 def deleteMe(self): 397 index = (self.file, self.line) 398 self.bpbynumber[self.number] = None 399 self.bplist[index].remove(self) 400 if not self.bplist[index]: 401 del self.bplist[index] 402 403 def enable(self): 404 self.enabled = 1 405 406 def disable(self): 407 self.enabled = 0 408 409 def bpprint(self, out = None): 410 if out is None: 411 out = sys.stdout 412 if self.temporary: 413 disp = 'del ' 414 else: 415 disp = 'keep ' 416 if self.enabled: 417 disp = disp + 'yes ' 418 else: 419 disp = disp + 'no ' 420 print >> out, '%-4dbreakpoint %s at %s:%d' % (self.number, 421 disp, 422 self.file, 423 self.line) 424 if self.cond: 425 print >> out, '\tstop only if %s' % (self.cond,) 426 if self.ignore: 427 print >> out, '\tignore next %d hits' % self.ignore 428 if self.hits: 429 if self.hits > 1: 430 ss = 's' 431 else: 432 ss = '' 433 print >> out, '\tbreakpoint already hit %d time%s' % (self.hits, ss) 434 435 436 def checkfuncname(b, frame): 437 if not b.funcname: 438 if b.line != frame.f_lineno: 439 return False 440 return True 441 if frame.f_code.co_name != b.funcname: 442 return False 443 if not b.func_first_executable_line: 444 b.func_first_executable_line = frame.f_lineno 445 if b.func_first_executable_line != frame.f_lineno: 446 return False 447 return True 448 449 450 def effective(file, line, frame): 451 possibles = Breakpoint.bplist[file, line] 452 for i in range(0, len(possibles)): 453 b = possibles[i] 454 if b.enabled == 0: 455 continue 456 if not checkfuncname(b, frame): 457 continue 458 b.hits = b.hits + 1 459 if not b.cond: 460 if b.ignore > 0: 461 b.ignore = b.ignore - 1 462 continue 463 else: 464 return (b, 1) 465 else: 466 try: 467 val = eval(b.cond, frame.f_globals, frame.f_locals) 468 if val: 469 if b.ignore > 0: 470 b.ignore = b.ignore - 1 471 else: 472 return (b, 1) 473 except: 474 return (b, 0) 475 476 return (None, None) 477 478 479 class Tdb(Bdb): 480 481 def user_call(self, frame, args): 482 name = frame.f_code.co_name 483 if not name: 484 name = '???' 485 print '+++ call', name, args 486 487 def user_line(self, frame): 488 import linecache 489 name = frame.f_code.co_name 490 if not name: 491 name = '???' 492 fn = self.canonic(frame.f_code.co_filename) 493 line = linecache.getline(fn, frame.f_lineno, frame.f_globals) 494 print '+++', fn, frame.f_lineno, name, ':', line.strip() 495 496 def user_return(self, frame, retval): 497 print '+++ return', retval 498 499 def user_exception(self, frame, exc_stuff): 500 print '+++ exception', exc_stuff 501 self.set_continue() 502 503 504 def foo(n): 505 print 'foo(', n, ')' 506 x = bar(n * 10) 507 print 'bar returned', x 508 509 510 def bar(a): 511 print 'bar(', a, ')' 512 return a / 2 513 514 515 def test(): 516 t = Tdb() 517 t.run('import bdb; bdb.foo(10)')