##
## some useful exception and reference leak count and traceback routines
## care of Sam Rushing (www.nightmare.com) of Medusa fame...
##
import string
import sys

whoCallsError = "whoCallsError"

#--------------------------------------------------------------
# Example use:
#
# import who_calls
# sys.stderr.write("%s\n" % who_calls.pretty_who_calls())
#
#--------------------------------------------------------------

def test():
  for i in range(1,1000):
    pretty_who_calls()

  print_top_100()

def who_calls_helper():
  tinfo = []
  exc_info = sys.exc_info()

  f = exc_info[2].tb_frame.f_back
  while f:
	  tinfo.append (
		  f.f_code.co_filename,
		  f.f_code.co_name,
		  f.f_lineno
		  )
	  f = f.f_back

  del exc_info
  return tinfo
  

def who_calls():
  try:
    raise whoCallsError
  except whoCallsError:
    tinfo = who_calls_helper()
  return tinfo

def pretty_who_calls():
	info = who_calls()
	buf = []

	for file,function,line in info[1:]:
		buf.append("   %s(%s): %s()" % (file,line,function))
		
	return string.join(buf,"\n")


# ---------------------------------------------------------------------------
# used for debugging.
# ---------------------------------------------------------------------------

def compact_traceback ():
	t,v,tb = sys.exc_info()
	tbinfo = []
	while 1:
		tbinfo.append (
			tb.tb_frame.f_code.co_filename,
			tb.tb_frame.f_code.co_name,				
			str(tb.tb_lineno)
			)
		tb = tb.tb_next
		if not tb:
			break

	# just to be safe
	del tb

	file, function, line = tbinfo[-1]
	info = '[' + string.join (
		map (
			lambda x: string.join (x, '|'),
			tbinfo
			),
		'] ['
		) + ']'

	return (file, function, line), str(t), str(v), info

## ----------------------------------------------------
## Refcount printing
		
import sys
import types

def get_refcounts():
        d = {}
        sys.modules
        # collect all classes
        for modname,m in sys.modules.items():
                for sym in dir(m):
                        o = getattr (m, sym)
                        if type(o) is types.ClassType:
                                d["%s:%s" % (modname,o.__name__)] = sys.getrefcount (o)
        # sort by refcount
        pairs = map (lambda x: (x[1],x[0]), d.items())
        pairs.sort()
        pairs.reverse()
        return pairs

def print_top_100():
        print_top_N(100)

def print_top_N(N):
        for n, c in get_refcounts()[:N]:
               sys.stderr.write('%10d %s\n' % (n, c))


