#-----------------------------------------------------------------------------
# Name: ErrorStack.py
# Purpose:
#
# Author: Riaan Booysen
#
# Created: 2000/05/29
# RCS-ID: $Id: ErrorStack.py,v 1.24 2007/07/02 15:01:04 riaan Exp $
# Copyright: (c) 1999 - 2007 Riaan Booysen
# Licence: GPL
#-----------------------------------------------------------------------------
import string, re, os, sys, pprint
import Utils
# XXX Make this more generic for pluggable parsers
if sys.version[:2] == '2.':
tb_id = 'Traceback (most recent call last):'
else:
tb_id = 'Traceback (innermost last):'
fileLine = re.compile(' File "(?P<filename>.+)", line (?P<lineno>[0-9]+)')
class StackEntry:
def __init__(self, file = '', lineNo = 0, line = '', error = ()):
self.file = file
self.line = line
self.lineNo = lineNo
self.error = error
def __repr__(self):
return 'File "%s", line %d\n%s' % (self.file, self.lineNo, self.line)
class RecFile(Utils.PseudoFile):
def write(self, s):
self.output.append(s)
def readlines(self):
return self.output
class StackErrorParser:
def __init__(self, lines):
self.lines = lines
self.stack = []
self.error = []
self.parse()
def printError(self):
print self.error
for se in self.stack:
print se
def __repr__(self):
return `self.error`+'\n'+pprint.pformat(self.stack)
# def write(self, s):
# self.lines.append(s)
class StdErrErrorParser(StackErrorParser):
def parse(self):
if len(self.lines):
line1 = self.lines.pop()
self.error[:] = list(line1.split(': '))
if len(self.error) == 1:
self.error.insert(0, 'String exception')
self.error[1] = self.error[1].strip()
for idx in range(len(self.lines)-1):
mo = fileLine.match(self.lines[idx].rstrip())
if mo:
self.stack.append(StackEntry(mo.group('filename'),
int(mo.group('lineno')), self.lines[idx + 1], self.error))
class PyCheckerErrorParser(StackErrorParser):
def parse(self):
import linecache
if self.lines:
pyCheckWarn = self.lines.pop()
try:
filename, lineNo, warng = eval(pyCheckWarn.strip(), {})
except:
pass
else:
self.error[:] = [warng]
self.stack.append(StackEntry(os.path.abspath(filename), lineNo,
linecache.getline(filename, lineNo), self.error))
class PyLintErrorParser(StackErrorParser):
def parse(self):
import linecache
if self.lines:
filename, lineNo, msg = self.lines.pop()
self.error[:] = [msg]
self.stack.append(StackEntry(os.path.abspath(filename), lineNo,
linecache.getline(filename, lineNo), self.error))
def buildLintWarningList(lines):
res = []
for line in lines:
res.append(PyLintErrorParser([line]))
return res
# return [PyLintErrorParser([line]) for line in lines]
# Limit stack size / processing time
# Zero to ignore limit
max_stack_depth = 100
max_lines_to_process = 20000
# XXX Look into speeding this up a bit :) !
class CrashTraceLogParser(StackErrorParser):
""" Build a stack from a trace file built with option -T """
def parse(self):
lines = self.lines
stack = self.stack = []
fileSize = len(lines)
self.error[:] = ['Core dump stack', 'trace file size: '+`fileSize`]
baseDir = lines[0].strip()
del lines[0]
lines.reverse()
cnt = 0
while len(lines):
cnt = cnt + 1
if max_lines_to_process and cnt >= max_lines_to_process:
break
line = lines[0]
del lines[0]
try:
file, lineno, frameid, event, arg = line.split('|', 4)
except:
print 'Error on line', cnt, line
break
if event == 'call':
if not os.path.isabs(file):
file = os.path.join(baseDir, file)
try: code = open(file).readlines()[int(lineno)-1]
except IOError: code = ''
stack.append( StackEntry(file, int(lineno), code) )
if max_stack_depth and len(stack) >= max_stack_depth:
break
elif event == 'return':
idx = 0
while 1:
try:
_file, _lineno, _frameid, _event, _rest = lines[idx].split('|', 4)
#print _file, _lineno, _frameid, _event
except Exception, error:
print 'Error on find', cnt, idx, lines[idx], str(error)
break
if _file == file and _frameid == frameid and _event == 'call':
del lines[:idx+1]
cnt = cnt + idx
break
idx = idx + 1
if idx >= len(lines):
print 'Call not found', file, lineno, frameid, len(lines)
del lines[:]
break
if len(stack):
stack.reverse()
else:
self.error[:] = ['Empty (resolved) stack', 'trace file size: '+`fileSize`]
def buildErrorList(lines, Parser = StdErrErrorParser):
errs = []
currerr = None
for line in lines:
if line.strip() == tb_id:
currerr = []
errs.append(currerr)
elif line:
if currerr is not None:
currerr.append(line)
if line[0] not in string.whitespace:
currerr = None
else:
# Try catch syntax errors
if line.startswith(' File '):
currerr = [line]
errs.append(currerr)
else:
currerr = None
errs.append([line])
res = []
for err in errs:
res.append(Parser(err))
return res
def errorList(stderr):
return buildErrorList(stderr.readlines())
def crashError(file):
try:
lines = open(file).readlines()
ctlp = CrashTraceLogParser(lines)
open(file+'.stack', 'w').write(`ctlp`)
return [ctlp]
except IOError:
return []
resp = {0 : 'failed', 1: 'succeeded'}
def test_buildErrorList(name, err_lines, answer):
err_list = str(buildErrorList(err_lines))
succ = err_list == answer
print '--Testing.', name, resp[succ]
if not succ:
print 'RESULT:'
print err_list
print 'ANSWER:'
print answer
print '--'
def test():
tb = [tb_id+'\n',
' File "Views\\AppViews.py", line 172, in OnRun\n',
' self.model.run()\n',
' File "EditorModels.py", line 548, in run\n',
" self.checkError(c, 'Ran')",
' File "EditorModels.py", line 513, in checkError\n',
' err.parse()\n',
'AttributeError: parse\n',
tb_id+'\n',
' File "Views\\AppViews.py", line 172, in OnRun\n',
' self.model.run()\n',
' File "EditorModels.py", line 548, in run\n',
" self.checkError(c, 'Ran')",
' File "EditorModels.py", line 513, in checkError\n',
' err.parse()\n',
'AttributeError: parse\n']
tb_answ = '''[['AttributeError', 'parse']
[File "Views\AppViews.py", line 172
self.model.run()
,
File "EditorModels.py", line 548
self.checkError(c, 'Ran'),
File "EditorModels.py", line 513
err.parse()
], ['AttributeError', 'parse']
[File "Views\AppViews.py", line 172
self.model.run()
,
File "EditorModels.py", line 548
self.checkError(c, 'Ran'),
File "EditorModels.py", line 513
err.parse()
]]'''
# Normal trace backs
test_buildErrorList('Long traceback', tb, tb_answ)
tb2 = [' File "Views\\SelectionTags.py", line 23\012',
' :\012',
' ^\012',
'SyntaxError: invalid syntax\012']
tb2_answ = '''[['SyntaxError', 'invalid syntax']
[File "Views\\SelectionTags.py", line 23
:
]]'''
# Syntax errors
test_buildErrorList('Short traceback', tb2, tb2_answ)
single_line_excp = "Exception exceptions.TypeError: 'call of non-function (type None)' in <method wxColourPtr.__del__ of wxColour instance at 03190B4C> ignored"
single_line_excp_answ = '''[['Exception exceptions.TypeError', "'call of non-function (type None)' in <method wxColourPtr.__del__ of wxColour instance at 03190B4C> ignored"]\n[]]'''
# One line 'warning' exceptions (e.g. wxPython objects deleted after
# the libraries have already unloaded
test_buildErrorList('Single line exception', [single_line_excp], single_line_excp_answ)
if __name__ == '__main__':
test()
|