| # Print tracebacks, with a dump of local variables. | 
 | # Also an interactive stack trace browser. | 
 |  | 
 | import sys | 
 | try: | 
 | 	import mac | 
 | 	os = mac | 
 | except NameError: | 
 | 	import posix | 
 | 	os = posix | 
 | from stat import * | 
 | import string | 
 |  | 
 | def br(): browser(sys.last_traceback) | 
 |  | 
 | def tb(): printtb(sys.last_traceback) | 
 |  | 
 | def browser(tb): | 
 | 	if not tb: | 
 | 		print 'No traceback.' | 
 | 		return | 
 | 	tblist = [] | 
 | 	while tb: | 
 | 		tblist.append(tb) | 
 | 		tb = tb.tb_next | 
 | 	ptr = len(tblist)-1 | 
 | 	tb = tblist[ptr] | 
 | 	while 1: | 
 | 		if tb <> tblist[ptr]: | 
 | 			tb = tblist[ptr] | 
 | 			print `ptr` + ':', | 
 | 			printtbheader(tb) | 
 | 		try: | 
 | 			line = raw_input('TB: ') | 
 | 		except KeyboardInterrupt: | 
 | 			print '\n[Interrupted]' | 
 | 			break | 
 | 		except EOFError: | 
 | 			print '\n[EOF]' | 
 | 			break | 
 | 		cmd = string.strip(line) | 
 | 		if cmd: | 
 | 			if cmd = 'quit': | 
 | 				break | 
 | 			elif cmd = 'list': | 
 | 				browserlist(tb) | 
 | 			elif cmd = 'up': | 
 | 				if ptr-1 >= 0: ptr = ptr-1 | 
 | 				else: print 'Bottom of stack.' | 
 | 			elif cmd = 'down': | 
 | 				if ptr+1 < len(tblist): ptr = ptr+1 | 
 | 				else: print 'Top of stack.' | 
 | 			elif cmd = 'locals': | 
 | 				printsymbols(tb.tb_frame.f_locals) | 
 | 			elif cmd = 'globals': | 
 | 				printsymbols(tb.tb_frame.f_globals) | 
 | 			elif cmd in ('?', 'help'): | 
 | 				browserhelp() | 
 | 			else: | 
 | 				browserexec(tb, cmd) | 
 |  | 
 | def browserlist(tb): | 
 | 	filename = tb.tb_frame.f_code.co_filename | 
 | 	lineno = tb.tb_lineno | 
 | 	last = lineno | 
 | 	first = max(1, last-10) | 
 | 	for i in range(first, last+1): | 
 | 		if i = lineno: prefix = '***' + string.rjust(`i`, 4) + ':' | 
 | 		else: prefix = string.rjust(`i`, 7) + ':' | 
 | 		line = readfileline(filename, i) | 
 | 		if line[-1:] = '\n': line = line[:-1] | 
 | 		print prefix + line | 
 |  | 
 | def browserexec(tb, cmd): | 
 | 	locals = tb.tb_frame.f_locals | 
 | 	globals = tb.tb_frame.f_globals | 
 | 	try: | 
 | 		exec(cmd+'\n', globals, locals) | 
 | 	except: | 
 | 		print '*** Exception:', | 
 | 		print sys.exc_type, | 
 | 		if sys.exc_value <> None: | 
 | 			print ':', sys.exc_value, | 
 | 		print | 
 | 		print 'Type help to get help.' | 
 |  | 
 | def browserhelp(): | 
 | 	print | 
 | 	print '    This is the traceback browser.  Commands are:' | 
 | 	print '        up      : move one level up in the call stack' | 
 | 	print '        down    : move one level down in the call stack' | 
 | 	print '        locals  : print all local variables at this level' | 
 | 	print '        globals : print all global variables at this level' | 
 | 	print '        list    : list source code around the failure' | 
 | 	print '        help    : print help (what you are reading now)' | 
 | 	print '        quit    : back to command interpreter' | 
 | 	print '    Typing any other 1-line statement will execute it' | 
 | 	print '    using the current level\'s symbol tables' | 
 | 	print | 
 |  | 
 | def printtb(tb): | 
 | 	while tb: | 
 | 		print1tb(tb) | 
 | 		tb = tb.tb_next | 
 |  | 
 | def print1tb(tb): | 
 | 	printtbheader(tb) | 
 | 	if tb.tb_frame.f_locals is not tb.tb_frame.f_globals: | 
 | 		printsymbols(tb.tb_frame.f_locals) | 
 |  | 
 | def printtbheader(tb): | 
 | 	filename = tb.tb_frame.f_code.co_filename | 
 | 	lineno = tb.tb_lineno | 
 | 	info = '"' + filename + '"(' + `lineno` + ')' | 
 | 	line = readfileline(filename, lineno) | 
 | 	if line: | 
 | 		info = info + ': ' + string.strip(line) | 
 | 	print info | 
 |  | 
 | def printsymbols(d): | 
 | 	keys = d.keys() | 
 | 	keys.sort() | 
 | 	for name in keys: | 
 | 		print '  ' + string.ljust(name, 12) + ':', | 
 | 		printobject(d[name], 4) | 
 | 		print | 
 |  | 
 | def printobject(v, maxlevel): | 
 | 	if v = None: | 
 | 		print 'None', | 
 | 	elif type(v) in (type(0), type(0.0)): | 
 | 		print v, | 
 | 	elif type(v) = type(''): | 
 | 		if len(v) > 20: | 
 | 			print `v[:17] + '...'`, | 
 | 		else: | 
 | 			print `v`, | 
 | 	elif type(v) = type(()): | 
 | 		print '(', | 
 | 		printlist(v, maxlevel) | 
 | 		print ')', | 
 | 	elif type(v) = type([]): | 
 | 		print '[', | 
 | 		printlist(v, maxlevel) | 
 | 		print ']', | 
 | 	elif type(v) = type({}): | 
 | 		print '{', | 
 | 		printdict(v, maxlevel) | 
 | 		print '}', | 
 | 	else: | 
 | 		print v, | 
 |  | 
 | def printlist(v, maxlevel): | 
 | 	n = len(v) | 
 | 	if n = 0: return | 
 | 	if maxlevel <= 0: | 
 | 		print '...', | 
 | 		return | 
 | 	for i in range(min(6, n)): | 
 | 		printobject(v[i], maxlevel-1) | 
 | 		if i+1 < n: print ',', | 
 | 	if n > 6: print '...', | 
 |  | 
 | def printdict(v, maxlevel): | 
 | 	keys = v.keys() | 
 | 	n = len(keys) | 
 | 	if n = 0: return | 
 | 	if maxlevel <= 0: | 
 | 		print '...', | 
 | 		return | 
 | 	keys.sort() | 
 | 	for i in range(min(6, n)): | 
 | 		key = keys[i] | 
 | 		print `key` + ':', | 
 | 		printobject(v[key], maxlevel-1) | 
 | 		if i+1 < n: print ',', | 
 | 	if n > 6: print '...', | 
 |  | 
 | _filecache = {} | 
 |  | 
 | def readfileline(filename, lineno): | 
 | 	try: | 
 | 		stat = os.stat(filename) | 
 | 	except os.error, msg: | 
 | 		print 'Cannot stat', filename, '--', msg | 
 | 		return '' | 
 | 	cache_ok = 0 | 
 | 	if _filecache.has_key(filename): | 
 | 		cached_stat, lines = _filecache[filename] | 
 | 		if stat[ST_SIZE] = cached_stat[ST_SIZE] and \ | 
 | 				stat[ST_MTIME] = cached_stat[ST_MTIME]: | 
 | 			cache_ok = 1 | 
 | 		else: | 
 | 			print 'Stale cache entry for', filename | 
 | 			del _filecache[filename] | 
 | 	if not cache_ok: | 
 | 		lines = readfilelines(filename) | 
 | 		if not lines: | 
 | 			return '' | 
 | 		_filecache[filename] = stat, lines | 
 | 	if 0 <= lineno-1 < len(lines): | 
 | 		return lines[lineno-1] | 
 | 	else: | 
 | 		print 'Line number out of range, last line is', len(lines) | 
 | 		return '' | 
 |  | 
 | def readfilelines(filename): | 
 | 	try: | 
 | 		fp = open(filename, 'r') | 
 | 	except: | 
 | 		print 'Cannot open', filename | 
 | 		return [] | 
 | 	lines = [] | 
 | 	while 1: | 
 | 		line = fp.readline() | 
 | 		if not line: break | 
 | 		lines.append(line) | 
 | 	if not lines: | 
 | 		print 'Empty file', filename | 
 | 	return lines |