test_ok1/py/code/traceback2.py

158 lines
5.2 KiB
Python

from __future__ import generators
import py
class TracebackEntry(object):
exprinfo = None
def __init__(self, rawentry):
self._rawentry = rawentry
self.frame = py.code.Frame(rawentry.tb_frame)
self.lineno = rawentry.tb_lineno - 1
self.relline = self.lineno - self.frame.code.firstlineno
def __repr__(self):
return "<TracebackEntry %s:%d>" %(self.frame.code.path, self.lineno+1)
def statement(self):
source = self.frame.code.fullsource
return source.getstatement(self.lineno)
statement = property(statement, None, None,
"statement of this traceback entry.")
def path(self):
return self.frame.code.path
path = property(path, None, None, "path to the full source code")
def getlocals(self):
return self.frame.f_locals
locals = property(getlocals, None, None, "locals of underlaying frame")
def reinterpret(self):
"""Reinterpret the failing statement and returns a detailed information
about what operations are performed."""
if self.exprinfo is None:
from py.__.magic import exprinfo
source = str(self.statement).strip()
x = exprinfo.interpret(source, self.frame, should_fail=True)
if not isinstance(x, str):
raise TypeError, "interpret returned non-string %r" % (x,)
self.exprinfo = x
return self.exprinfo
def getfirstlinesource(self):
return self.frame.code.firstlineno
def getsource(self):
""" return failing source code. """
source = self.frame.code.fullsource
start = self.getfirstlinesource()
end = self.lineno
try:
_, end = source.getstatementrange(end)
except IndexError:
end = self.lineno + 1
# heuristic to stop displaying source on e.g.
# if something: # assume this causes a NameError
# # _this_ lines and the one
# below we don't want from entry.getsource()
for i in range(self.lineno, end):
if source[i].rstrip().endswith(':'):
end = i + 1
break
return source[start:end]
source = property(getsource)
def ishidden(self):
try:
return self.frame.eval("__tracebackhide__")
except (SystemExit, KeyboardInterrupt):
raise
except:
return False
def __str__(self):
try:
fn = str(self.path)
except py.error.Error:
fn = '???'
name = self.frame.code.name
try:
line = str(self.statement).lstrip()
except EnvironmentError, e:
line = "<could not get sourceline>"
return " File %r:%d in %s\n %s\n" %(fn, self.lineno+1, name, line)
def name(self):
return self.frame.code.raw.co_name
name = property(name, None, None, "co_name of underlaying code")
class Traceback(list):
""" Traceback objects encapsulate and offer higher level
access to Traceback entries.
"""
Entry = TracebackEntry
def __init__(self, tb):
""" initialize from given python traceback object. """
if hasattr(tb, 'tb_next'):
def f(cur):
while cur is not None:
yield self.Entry(cur)
cur = cur.tb_next
list.__init__(self, f(tb))
else:
list.__init__(self, tb)
def cut(self, path=None, lineno=None, firstlineno=None):
for x in self:
if ((path is None or x.frame.code.path == path) and
(lineno is None or x.lineno == lineno) and
(firstlineno is None or x.frame.code.firstlineno == firstlineno)):
return Traceback(x._rawentry)
return self
def __getitem__(self, key):
val = super(Traceback, self).__getitem__(key)
if isinstance(key, type(slice(0))):
val = self.__class__(val)
return val
def filter(self, fn=lambda x: not x.ishidden()):
return Traceback(filter(fn, self))
def getcrashentry(self):
""" return last non-hidden traceback entry that lead
to the exception of a traceback.
"""
tb = self.filter()
if not tb:
tb = self
return tb[-1]
def recursionindex(self):
cache = {}
for i, entry in py.builtin.enumerate(self):
key = entry.frame.code.path, entry.lineno
#print "checking for recursion at", key
l = cache.setdefault(key, [])
if l:
f = entry.frame
loc = f.f_locals
for otherloc in l:
if f.is_true(f.eval(co_equal,
__recursioncache_locals_1=loc,
__recursioncache_locals_2=otherloc)):
return i
l.append(entry.frame.f_locals)
return None
# def __str__(self):
# for x in self
# l = []
## for func, entry in self._tblist:
# l.append(entry.display())
# return "".join(l)
co_equal = compile('__recursioncache_locals_1 == __recursioncache_locals_2',
'?', 'eval')