# Print tracebacks, with a dump of local variables.
# Also an interactive stack trace browser.

import sys
import os
from stat import *
import string
import linecache

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 = linecache.getline(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 = linecache.getline(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 '...',
