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 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
|
""" This file intends to gather all methods of representing
failures/tracebacks etc. which should be used among
all terminal-based reporters. This methods should be general,
to allow further use outside the pylib
"""
import py
from py.__.code import safe_repr
class Presenter(object):
""" Class used for presentation of various objects,
sharing common output style
"""
def __init__(self, out, config):
""" out is a file-like object (we can write to it)
"""
assert hasattr(out, 'write')
self.out = out
self.config = config
def repr_source(self, source, marker=">", marker_location=-1):
""" This one represents piece of source with possible
marker at requested position
"""
if isinstance(source, str):
# why the hell, string is iterable?
source = source.split("\n")
if marker_location < 0:
marker_location += len(source)
if marker_location < 0:
marker_location = 0
if marker_location >= len(source):
marker_location = len(source) - 1
for i in range(len(source)):
if i == marker_location:
prefix = marker + " "
else:
prefix = " "
self.out.line(prefix + source[i])
def repr_item_info(self, item):
""" This method represents py.test.collect.Item info (path and module)
"""
root = item.fspath
modpath = item._getmodpath()
try:
fn, lineno = item._getpathlineno()
except TypeError:
assert isinstance(item.parent, py.test.collect.Generator)
# a generative test yielded a non-callable
fn, lineno = item.parent._getpathlineno()
if root == fn:
self.out.sep("_", "entrypoint: %s" %(modpath))
else:
self.out.sep("_", "entrypoint: %s %s" %(root.basename, modpath))
def repr_failure_explanation(self, excinfo, source):
try:
s = str(source.getstatement(len(source)-1))
except KeyboardInterrupt:
raise
except:
try:
s = str(source[-1])
except IndexError:
s = "<Cannot get source>"
indent = " " * (4 + (len(s) - len(s.lstrip())))
# get the real exception information out
lines = excinfo.exconly(tryshort=True).split('\n')
self.out.line('>' + indent[:-1] + lines.pop(0))
for x in lines:
self.out.line(indent + x)
def getentrysource(self, entry):
try:
source = entry.getsource()
except py.error.ENOENT:
source = py.code.Source("[failure to get at sourcelines from %r]\n" % entry)
return source.deindent()
def repr_locals(self, f_locals):
if self.config.option.showlocals:
self.out.sep('- ', 'locals')
for name, value in f_locals.items():
if name == '__builtins__':
self.out.line("__builtins__ = <builtins>")
else:
# This formatting could all be handled by the _repr() function, which is
# only repr.Repr in disguise, so is very configurable.
str_repr = safe_repr._repr(value)
if len(str_repr) < 70 or not isinstance(value,
(list, tuple, dict)):
self.out.line("%-10s = %s" %(name, str_repr))
else:
self.out.line("%-10s =\\" % (name,))
py.std.pprint.pprint(value, stream=self.out)
def repr_failure_tblong(self, item, excinfo, traceback, out_err_reporter):
if not self.config.option.nomagic and excinfo.errisinstance(RuntimeError):
recursionindex = traceback.recursionindex()
else:
recursionindex = None
last = traceback[-1]
first = traceback[0]
for index, entry in py.builtin.enumerate(traceback):
if entry == first:
if item:
self.repr_item_info(item)
self.out.line()
else:
self.out.line("")
source = self.getentrysource(entry)
firstsourceline = entry.getfirstlinesource()
marker_location = entry.lineno - firstsourceline
if entry == last:
self.repr_source(source, 'E', marker_location)
self.repr_failure_explanation(excinfo, source)
else:
self.repr_source(source, '>', marker_location)
self.out.line("")
self.out.line("[%s:%d]" %(entry.path, entry.lineno+1))
self.repr_locals(entry.locals)
# trailing info
if entry == last:
out_err_reporter()
self.out.sep("_")
else:
self.out.sep("_ ")
if index == recursionindex:
self.out.line("Recursion detected (same locals & position)")
self.out.sep("!")
break
def repr_failure_tbshort(self, item, excinfo, traceback, out_err_reporter):
# print a Python-style short traceback
if not self.config.option.nomagic and excinfo.errisinstance(RuntimeError):
recursionindex = traceback.recursionindex()
else:
recursionindex = None
last = traceback[-1]
first = traceback[0]
self.out.line()
for index, entry in py.builtin.enumerate(traceback):
path = entry.path.basename
firstsourceline = entry.getfirstlinesource()
relline = entry.lineno - firstsourceline
self.out.line(' File "%s", line %d, in %s' % (
path, entry.lineno+1, entry.name))
try:
source = entry.getsource().lines
except py.error.ENOENT:
source = ["?"]
else:
try:
if len(source) > 1:
source = source[relline]
except IndexError:
source = []
if entry == last:
if source:
self.repr_source(source, 'E')
self.repr_failure_explanation(excinfo, source)
else:
if source:
self.repr_source(source, ' ')
self.repr_locals(entry.locals)
# trailing info
if entry == last:
out_err_reporter()
self.out.sep("_")
else:
if index == recursionindex:
self.out.line("Recursion detected (same locals & position)")
self.out.sep("!")
break
# the following is only used by the combination '--pdb --tb=no'
repr_failure_tbno = repr_failure_tbshort
|