1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
|
"""
Formats nose output into format easily parsable by machine.
It is intended to be use to integrate nose with your IDE such as Vim.
"""
import re
import os.path
import traceback
from nose.plugins import Plugin
__all__ = ['NoseMachineReadableOutput']
try:
import doctest
doctest_fname = re.sub('\.py.?$', '.py', doctest.__file__)
del doctest
except ImportError:
doctest_fname = None
class dummystream:
def write(self, *arg):
pass
def writeln(self, *arg):
pass
def flush(self):
pass
def is_doctest_traceback(fname):
return fname == doctest_fname
class PluginError(Exception):
pass
class NoseMachineReadableOutput(Plugin):
"""
Output errors and failures in a machine-readable way.
"""
name = 'machineout'
doctest_failure_re = re.compile('File "([^"]+)", line (\d+), in ([^\n]+)\n(.+)',
re.DOTALL)
def __init__(self):
super(NoseMachineReadableOutput, self).__init__()
self.basepath = os.getcwd()
def add_options(self, parser, env):
super(NoseMachineReadableOutput, self).add_options(parser, env)
parser.add_option("--machine-output", action="store_true",
dest="machine_output",
default=False,
help="Reports test results in easily parsable format.")
def configure(self, options, conf):
super(NoseMachineReadableOutput, self).configure(options, conf)
self.enabled = options.machine_output
def addSkip(self, test):
pass
def addDeprecated(self, test):
pass
def addError(self, test, err):
self.addFormatted('error', err)
def addFormatted(self, etype, err):
exctype, value, tb = err
fulltb = traceback.extract_tb(tb)
fallback = fulltb[-1]
try:
while True:
fname, lineno, funname, msg = fulltb.pop()
if fname.startswith(self.basepath):
break
except IndexError:
fname, lineno, funname, msg = fallback
# explicit support for doctests is needed
if is_doctest_traceback(fname):
# doctest traceback includes pre-formatted error message
# which we parse (in a very crude way).
n = value.args[0].rindex('-'*20)
formatted_msg = value.args[0][n+20+1:]
m = self.doctest_failure_re.match(formatted_msg)
if not m:
raise RuntimeError("Can't parse doctest output: %r" % value.args[0])
fname, lineno, funname, msg = m.groups()
if '.' in funname: # strip module package name, if any
funname = funname.split('.')[-1]
lineno = int(lineno)
lines = msg.split('\n')
msg0 = lines[0]
else:
lines = traceback.format_exception_only(exctype, value)
lines = [line.strip('\n') for line in lines]
msg0 = lines[0]
fname = self.format_testfname(fname)
prefix = "%s:%d" % (fname, lineno)
self.stream.writeln("%s: In %s" % (fname, funname))
self.stream.writeln("%s: %s: %s" % (prefix, etype, msg0))
if len(lines) > 1:
pad = ' '*(len(etype)+1)
for line in lines[1:]:
self.stream.writeln("%s: %s %s" % (prefix, pad, line))
def format_testfname(self, fname):
if fname.startswith(self.basepath):
return fname[len(self.basepath)+1:]
return fname
def addFailure(self, test, err):
self.addFormatted('fail', err)
def setOutputStream(self, stream):
self.stream = stream
return dummystream()
|