~ibmcharmers/charms/xenial/ibm-cinder-storwize-svc/trunk

« back to all changes in this revision

Viewing changes to .tox/py35/lib/python3.5/site-packages/_pytest/_code/code.py

  • Committer: Ankammarao
  • Date: 2017-03-06 05:11:42 UTC
  • Revision ID: achittet@in.ibm.com-20170306051142-dpg27z4es1k56hfn
Marked tests folder executable

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
import sys
 
2
from inspect import CO_VARARGS, CO_VARKEYWORDS
 
3
import re
 
4
from weakref import ref
 
5
 
 
6
import py
 
7
builtin_repr = repr
 
8
 
 
9
reprlib = py.builtin._tryimport('repr', 'reprlib')
 
10
 
 
11
if sys.version_info[0] >= 3:
 
12
    from traceback import format_exception_only
 
13
else:
 
14
    from ._py2traceback import format_exception_only
 
15
 
 
16
 
 
17
class Code(object):
 
18
    """ wrapper around Python code objects """
 
19
    def __init__(self, rawcode):
 
20
        if not hasattr(rawcode, "co_filename"):
 
21
            rawcode = getrawcode(rawcode)
 
22
        try:
 
23
            self.filename = rawcode.co_filename
 
24
            self.firstlineno = rawcode.co_firstlineno - 1
 
25
            self.name = rawcode.co_name
 
26
        except AttributeError:
 
27
            raise TypeError("not a code object: %r" %(rawcode,))
 
28
        self.raw = rawcode
 
29
 
 
30
    def __eq__(self, other):
 
31
        return self.raw == other.raw
 
32
 
 
33
    __hash__ = None
 
34
 
 
35
    def __ne__(self, other):
 
36
        return not self == other
 
37
 
 
38
    @property
 
39
    def path(self):
 
40
        """ return a path object pointing to source code (note that it
 
41
        might not point to an actually existing file). """
 
42
        try:
 
43
            p = py.path.local(self.raw.co_filename)
 
44
            # maybe don't try this checking
 
45
            if not p.check():
 
46
                raise OSError("py.path check failed.")
 
47
        except OSError:
 
48
            # XXX maybe try harder like the weird logic
 
49
            # in the standard lib [linecache.updatecache] does?
 
50
            p = self.raw.co_filename
 
51
 
 
52
        return p
 
53
 
 
54
    @property
 
55
    def fullsource(self):
 
56
        """ return a _pytest._code.Source object for the full source file of the code
 
57
        """
 
58
        from _pytest._code import source
 
59
        full, _ = source.findsource(self.raw)
 
60
        return full
 
61
 
 
62
    def source(self):
 
63
        """ return a _pytest._code.Source object for the code object's source only
 
64
        """
 
65
        # return source only for that part of code
 
66
        import _pytest._code
 
67
        return _pytest._code.Source(self.raw)
 
68
 
 
69
    def getargs(self, var=False):
 
70
        """ return a tuple with the argument names for the code object
 
71
 
 
72
            if 'var' is set True also return the names of the variable and
 
73
            keyword arguments when present
 
74
        """
 
75
        # handfull shortcut for getting args
 
76
        raw = self.raw
 
77
        argcount = raw.co_argcount
 
78
        if var:
 
79
            argcount += raw.co_flags & CO_VARARGS
 
80
            argcount += raw.co_flags & CO_VARKEYWORDS
 
81
        return raw.co_varnames[:argcount]
 
82
 
 
83
class Frame(object):
 
84
    """Wrapper around a Python frame holding f_locals and f_globals
 
85
    in which expressions can be evaluated."""
 
86
 
 
87
    def __init__(self, frame):
 
88
        self.lineno = frame.f_lineno - 1
 
89
        self.f_globals = frame.f_globals
 
90
        self.f_locals = frame.f_locals
 
91
        self.raw = frame
 
92
        self.code = Code(frame.f_code)
 
93
 
 
94
    @property
 
95
    def statement(self):
 
96
        """ statement this frame is at """
 
97
        import _pytest._code
 
98
        if self.code.fullsource is None:
 
99
            return _pytest._code.Source("")
 
100
        return self.code.fullsource.getstatement(self.lineno)
 
101
 
 
102
    def eval(self, code, **vars):
 
103
        """ evaluate 'code' in the frame
 
104
 
 
105
            'vars' are optional additional local variables
 
106
 
 
107
            returns the result of the evaluation
 
108
        """
 
109
        f_locals = self.f_locals.copy()
 
110
        f_locals.update(vars)
 
111
        return eval(code, self.f_globals, f_locals)
 
112
 
 
113
    def exec_(self, code, **vars):
 
114
        """ exec 'code' in the frame
 
115
 
 
116
            'vars' are optiona; additional local variables
 
117
        """
 
118
        f_locals = self.f_locals.copy()
 
119
        f_locals.update(vars)
 
120
        py.builtin.exec_(code, self.f_globals, f_locals )
 
121
 
 
122
    def repr(self, object):
 
123
        """ return a 'safe' (non-recursive, one-line) string repr for 'object'
 
124
        """
 
125
        return py.io.saferepr(object)
 
126
 
 
127
    def is_true(self, object):
 
128
        return object
 
129
 
 
130
    def getargs(self, var=False):
 
131
        """ return a list of tuples (name, value) for all arguments
 
132
 
 
133
            if 'var' is set True also include the variable and keyword
 
134
            arguments when present
 
135
        """
 
136
        retval = []
 
137
        for arg in self.code.getargs(var):
 
138
            try:
 
139
                retval.append((arg, self.f_locals[arg]))
 
140
            except KeyError:
 
141
                pass     # this can occur when using Psyco
 
142
        return retval
 
143
 
 
144
class TracebackEntry(object):
 
145
    """ a single entry in a traceback """
 
146
 
 
147
    _repr_style = None
 
148
    exprinfo = None
 
149
 
 
150
    def __init__(self, rawentry, excinfo=None):
 
151
        self._excinfo = excinfo
 
152
        self._rawentry = rawentry
 
153
        self.lineno = rawentry.tb_lineno - 1
 
154
 
 
155
    def set_repr_style(self, mode):
 
156
        assert mode in ("short", "long")
 
157
        self._repr_style = mode
 
158
 
 
159
    @property
 
160
    def frame(self):
 
161
        import _pytest._code
 
162
        return _pytest._code.Frame(self._rawentry.tb_frame)
 
163
 
 
164
    @property
 
165
    def relline(self):
 
166
        return self.lineno - self.frame.code.firstlineno
 
167
 
 
168
    def __repr__(self):
 
169
        return "<TracebackEntry %s:%d>" %(self.frame.code.path, self.lineno+1)
 
170
 
 
171
    @property
 
172
    def statement(self):
 
173
        """ _pytest._code.Source object for the current statement """
 
174
        source = self.frame.code.fullsource
 
175
        return source.getstatement(self.lineno)
 
176
 
 
177
    @property
 
178
    def path(self):
 
179
        """ path to the source code """
 
180
        return self.frame.code.path
 
181
 
 
182
    def getlocals(self):
 
183
        return self.frame.f_locals
 
184
    locals = property(getlocals, None, None, "locals of underlaying frame")
 
185
 
 
186
    def getfirstlinesource(self):
 
187
        # on Jython this firstlineno can be -1 apparently
 
188
        return max(self.frame.code.firstlineno, 0)
 
189
 
 
190
    def getsource(self, astcache=None):
 
191
        """ return failing source code. """
 
192
        # we use the passed in astcache to not reparse asttrees
 
193
        # within exception info printing
 
194
        from _pytest._code.source import getstatementrange_ast
 
195
        source = self.frame.code.fullsource
 
196
        if source is None:
 
197
            return None
 
198
        key = astnode = None
 
199
        if astcache is not None:
 
200
            key = self.frame.code.path
 
201
            if key is not None:
 
202
                astnode = astcache.get(key, None)
 
203
        start = self.getfirstlinesource()
 
204
        try:
 
205
            astnode, _, end = getstatementrange_ast(self.lineno, source,
 
206
                                                    astnode=astnode)
 
207
        except SyntaxError:
 
208
            end = self.lineno + 1
 
209
        else:
 
210
            if key is not None:
 
211
                astcache[key] = astnode
 
212
        return source[start:end]
 
213
 
 
214
    source = property(getsource)
 
215
 
 
216
    def ishidden(self):
 
217
        """ return True if the current frame has a var __tracebackhide__
 
218
            resolving to True
 
219
 
 
220
            If __tracebackhide__ is a callable, it gets called with the
 
221
            ExceptionInfo instance and can decide whether to hide the traceback.
 
222
 
 
223
            mostly for internal use
 
224
        """
 
225
        try:
 
226
            tbh = self.frame.f_locals['__tracebackhide__']
 
227
        except KeyError:
 
228
            try:
 
229
                tbh = self.frame.f_globals['__tracebackhide__']
 
230
            except KeyError:
 
231
                return False
 
232
 
 
233
        if py.builtin.callable(tbh):
 
234
            return tbh(None if self._excinfo is None else self._excinfo())
 
235
        else:
 
236
            return tbh
 
237
 
 
238
    def __str__(self):
 
239
        try:
 
240
            fn = str(self.path)
 
241
        except py.error.Error:
 
242
            fn = '???'
 
243
        name = self.frame.code.name
 
244
        try:
 
245
            line = str(self.statement).lstrip()
 
246
        except KeyboardInterrupt:
 
247
            raise
 
248
        except:
 
249
            line = "???"
 
250
        return "  File %r:%d in %s\n  %s\n" %(fn, self.lineno+1, name, line)
 
251
 
 
252
    def name(self):
 
253
        return self.frame.code.raw.co_name
 
254
    name = property(name, None, None, "co_name of underlaying code")
 
255
 
 
256
class Traceback(list):
 
257
    """ Traceback objects encapsulate and offer higher level
 
258
        access to Traceback entries.
 
259
    """
 
260
    Entry = TracebackEntry
 
261
    def __init__(self, tb, excinfo=None):
 
262
        """ initialize from given python traceback object and ExceptionInfo """
 
263
        self._excinfo = excinfo
 
264
        if hasattr(tb, 'tb_next'):
 
265
            def f(cur):
 
266
                while cur is not None:
 
267
                    yield self.Entry(cur, excinfo=excinfo)
 
268
                    cur = cur.tb_next
 
269
            list.__init__(self, f(tb))
 
270
        else:
 
271
            list.__init__(self, tb)
 
272
 
 
273
    def cut(self, path=None, lineno=None, firstlineno=None, excludepath=None):
 
274
        """ return a Traceback instance wrapping part of this Traceback
 
275
 
 
276
            by provding any combination of path, lineno and firstlineno, the
 
277
            first frame to start the to-be-returned traceback is determined
 
278
 
 
279
            this allows cutting the first part of a Traceback instance e.g.
 
280
            for formatting reasons (removing some uninteresting bits that deal
 
281
            with handling of the exception/traceback)
 
282
        """
 
283
        for x in self:
 
284
            code = x.frame.code
 
285
            codepath = code.path
 
286
            if ((path is None or codepath == path) and
 
287
                (excludepath is None or not hasattr(codepath, 'relto') or
 
288
                 not codepath.relto(excludepath)) and
 
289
                (lineno is None or x.lineno == lineno) and
 
290
                (firstlineno is None or x.frame.code.firstlineno == firstlineno)):
 
291
                return Traceback(x._rawentry, self._excinfo)
 
292
        return self
 
293
 
 
294
    def __getitem__(self, key):
 
295
        val = super(Traceback, self).__getitem__(key)
 
296
        if isinstance(key, type(slice(0))):
 
297
            val = self.__class__(val)
 
298
        return val
 
299
 
 
300
    def filter(self, fn=lambda x: not x.ishidden()):
 
301
        """ return a Traceback instance with certain items removed
 
302
 
 
303
            fn is a function that gets a single argument, a TracebackEntry
 
304
            instance, and should return True when the item should be added
 
305
            to the Traceback, False when not
 
306
 
 
307
            by default this removes all the TracebackEntries which are hidden
 
308
            (see ishidden() above)
 
309
        """
 
310
        return Traceback(filter(fn, self), self._excinfo)
 
311
 
 
312
    def getcrashentry(self):
 
313
        """ return last non-hidden traceback entry that lead
 
314
        to the exception of a traceback.
 
315
        """
 
316
        for i in range(-1, -len(self)-1, -1):
 
317
            entry = self[i]
 
318
            if not entry.ishidden():
 
319
                return entry
 
320
        return self[-1]
 
321
 
 
322
    def recursionindex(self):
 
323
        """ return the index of the frame/TracebackEntry where recursion
 
324
            originates if appropriate, None if no recursion occurred
 
325
        """
 
326
        cache = {}
 
327
        for i, entry in enumerate(self):
 
328
            # id for the code.raw is needed to work around
 
329
            # the strange metaprogramming in the decorator lib from pypi
 
330
            # which generates code objects that have hash/value equality
 
331
            #XXX needs a test
 
332
            key = entry.frame.code.path, id(entry.frame.code.raw), entry.lineno
 
333
            #print "checking for recursion at", key
 
334
            l = cache.setdefault(key, [])
 
335
            if l:
 
336
                f = entry.frame
 
337
                loc = f.f_locals
 
338
                for otherloc in l:
 
339
                    if f.is_true(f.eval(co_equal,
 
340
                        __recursioncache_locals_1=loc,
 
341
                        __recursioncache_locals_2=otherloc)):
 
342
                        return i
 
343
            l.append(entry.frame.f_locals)
 
344
        return None
 
345
 
 
346
 
 
347
co_equal = compile('__recursioncache_locals_1 == __recursioncache_locals_2',
 
348
                   '?', 'eval')
 
349
 
 
350
class ExceptionInfo(object):
 
351
    """ wraps sys.exc_info() objects and offers
 
352
        help for navigating the traceback.
 
353
    """
 
354
    _striptext = ''
 
355
    def __init__(self, tup=None, exprinfo=None):
 
356
        import _pytest._code
 
357
        if tup is None:
 
358
            tup = sys.exc_info()
 
359
            if exprinfo is None and isinstance(tup[1], AssertionError):
 
360
                exprinfo = getattr(tup[1], 'msg', None)
 
361
                if exprinfo is None:
 
362
                    exprinfo = py._builtin._totext(tup[1])
 
363
                if exprinfo and exprinfo.startswith('assert '):
 
364
                    self._striptext = 'AssertionError: '
 
365
        self._excinfo = tup
 
366
        #: the exception class
 
367
        self.type = tup[0]
 
368
        #: the exception instance
 
369
        self.value = tup[1]
 
370
        #: the exception raw traceback
 
371
        self.tb = tup[2]
 
372
        #: the exception type name
 
373
        self.typename = self.type.__name__
 
374
        #: the exception traceback (_pytest._code.Traceback instance)
 
375
        self.traceback = _pytest._code.Traceback(self.tb, excinfo=ref(self))
 
376
 
 
377
    def __repr__(self):
 
378
        return "<ExceptionInfo %s tblen=%d>" % (self.typename, len(self.traceback))
 
379
 
 
380
    def exconly(self, tryshort=False):
 
381
        """ return the exception as a string
 
382
 
 
383
            when 'tryshort' resolves to True, and the exception is a
 
384
            _pytest._code._AssertionError, only the actual exception part of
 
385
            the exception representation is returned (so 'AssertionError: ' is
 
386
            removed from the beginning)
 
387
        """
 
388
        lines = format_exception_only(self.type, self.value)
 
389
        text = ''.join(lines)
 
390
        text = text.rstrip()
 
391
        if tryshort:
 
392
            if text.startswith(self._striptext):
 
393
                text = text[len(self._striptext):]
 
394
        return text
 
395
 
 
396
    def errisinstance(self, exc):
 
397
        """ return True if the exception is an instance of exc """
 
398
        return isinstance(self.value, exc)
 
399
 
 
400
    def _getreprcrash(self):
 
401
        exconly = self.exconly(tryshort=True)
 
402
        entry = self.traceback.getcrashentry()
 
403
        path, lineno = entry.frame.code.raw.co_filename, entry.lineno
 
404
        return ReprFileLocation(path, lineno+1, exconly)
 
405
 
 
406
    def getrepr(self, showlocals=False, style="long",
 
407
            abspath=False, tbfilter=True, funcargs=False):
 
408
        """ return str()able representation of this exception info.
 
409
            showlocals: show locals per traceback entry
 
410
            style: long|short|no|native traceback style
 
411
            tbfilter: hide entries (where __tracebackhide__ is true)
 
412
 
 
413
            in case of style==native, tbfilter and showlocals is ignored.
 
414
        """
 
415
        if style == 'native':
 
416
            return ReprExceptionInfo(ReprTracebackNative(
 
417
                py.std.traceback.format_exception(
 
418
                    self.type,
 
419
                    self.value,
 
420
                    self.traceback[0]._rawentry,
 
421
                )), self._getreprcrash())
 
422
 
 
423
        fmt = FormattedExcinfo(showlocals=showlocals, style=style,
 
424
            abspath=abspath, tbfilter=tbfilter, funcargs=funcargs)
 
425
        return fmt.repr_excinfo(self)
 
426
 
 
427
    def __str__(self):
 
428
        entry = self.traceback[-1]
 
429
        loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly())
 
430
        return str(loc)
 
431
 
 
432
    def __unicode__(self):
 
433
        entry = self.traceback[-1]
 
434
        loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly())
 
435
        return unicode(loc)
 
436
 
 
437
    def match(self, regexp):
 
438
        """
 
439
        Match the regular expression 'regexp' on the string representation of
 
440
        the exception. If it matches then True is returned (so that it is
 
441
        possible to write 'assert excinfo.match()'). If it doesn't match an
 
442
        AssertionError is raised.
 
443
        """
 
444
        __tracebackhide__ = True
 
445
        if not re.search(regexp, str(self.value)):
 
446
            assert 0, "Pattern '{0!s}' not found in '{1!s}'".format(
 
447
                regexp, self.value)
 
448
        return True
 
449
 
 
450
 
 
451
class FormattedExcinfo(object):
 
452
    """ presenting information about failing Functions and Generators. """
 
453
    # for traceback entries
 
454
    flow_marker = ">"
 
455
    fail_marker = "E"
 
456
 
 
457
    def __init__(self, showlocals=False, style="long", abspath=True, tbfilter=True, funcargs=False):
 
458
        self.showlocals = showlocals
 
459
        self.style = style
 
460
        self.tbfilter = tbfilter
 
461
        self.funcargs = funcargs
 
462
        self.abspath = abspath
 
463
        self.astcache = {}
 
464
 
 
465
    def _getindent(self, source):
 
466
        # figure out indent for given source
 
467
        try:
 
468
            s = str(source.getstatement(len(source)-1))
 
469
        except KeyboardInterrupt:
 
470
            raise
 
471
        except:
 
472
            try:
 
473
                s = str(source[-1])
 
474
            except KeyboardInterrupt:
 
475
                raise
 
476
            except:
 
477
                return 0
 
478
        return 4 + (len(s) - len(s.lstrip()))
 
479
 
 
480
    def _getentrysource(self, entry):
 
481
        source = entry.getsource(self.astcache)
 
482
        if source is not None:
 
483
            source = source.deindent()
 
484
        return source
 
485
 
 
486
    def _saferepr(self, obj):
 
487
        return py.io.saferepr(obj)
 
488
 
 
489
    def repr_args(self, entry):
 
490
        if self.funcargs:
 
491
            args = []
 
492
            for argname, argvalue in entry.frame.getargs(var=True):
 
493
                args.append((argname, self._saferepr(argvalue)))
 
494
            return ReprFuncArgs(args)
 
495
 
 
496
    def get_source(self, source, line_index=-1, excinfo=None, short=False):
 
497
        """ return formatted and marked up source lines. """
 
498
        import _pytest._code
 
499
        lines = []
 
500
        if source is None or line_index >= len(source.lines):
 
501
            source = _pytest._code.Source("???")
 
502
            line_index = 0
 
503
        if line_index < 0:
 
504
            line_index += len(source)
 
505
        space_prefix = "    "
 
506
        if short:
 
507
            lines.append(space_prefix + source.lines[line_index].strip())
 
508
        else:
 
509
            for line in source.lines[:line_index]:
 
510
                lines.append(space_prefix + line)
 
511
            lines.append(self.flow_marker + "   " + source.lines[line_index])
 
512
            for line in source.lines[line_index+1:]:
 
513
                lines.append(space_prefix + line)
 
514
        if excinfo is not None:
 
515
            indent = 4 if short else self._getindent(source)
 
516
            lines.extend(self.get_exconly(excinfo, indent=indent, markall=True))
 
517
        return lines
 
518
 
 
519
    def get_exconly(self, excinfo, indent=4, markall=False):
 
520
        lines = []
 
521
        indent = " " * indent
 
522
        # get the real exception information out
 
523
        exlines = excinfo.exconly(tryshort=True).split('\n')
 
524
        failindent = self.fail_marker + indent[1:]
 
525
        for line in exlines:
 
526
            lines.append(failindent + line)
 
527
            if not markall:
 
528
                failindent = indent
 
529
        return lines
 
530
 
 
531
    def repr_locals(self, locals):
 
532
        if self.showlocals:
 
533
            lines = []
 
534
            keys = [loc for loc in locals if loc[0] != "@"]
 
535
            keys.sort()
 
536
            for name in keys:
 
537
                value = locals[name]
 
538
                if name == '__builtins__':
 
539
                    lines.append("__builtins__ = <builtins>")
 
540
                else:
 
541
                    # This formatting could all be handled by the
 
542
                    # _repr() function, which is only reprlib.Repr in
 
543
                    # disguise, so is very configurable.
 
544
                    str_repr = self._saferepr(value)
 
545
                    #if len(str_repr) < 70 or not isinstance(value,
 
546
                    #                            (list, tuple, dict)):
 
547
                    lines.append("%-10s = %s" %(name, str_repr))
 
548
                    #else:
 
549
                    #    self._line("%-10s =\\" % (name,))
 
550
                    #    # XXX
 
551
                    #    py.std.pprint.pprint(value, stream=self.excinfowriter)
 
552
            return ReprLocals(lines)
 
553
 
 
554
    def repr_traceback_entry(self, entry, excinfo=None):
 
555
        import _pytest._code
 
556
        source = self._getentrysource(entry)
 
557
        if source is None:
 
558
            source = _pytest._code.Source("???")
 
559
            line_index = 0
 
560
        else:
 
561
            # entry.getfirstlinesource() can be -1, should be 0 on jython
 
562
            line_index = entry.lineno - max(entry.getfirstlinesource(), 0)
 
563
 
 
564
        lines = []
 
565
        style = entry._repr_style
 
566
        if style is None:
 
567
            style = self.style
 
568
        if style in ("short", "long"):
 
569
            short = style == "short"
 
570
            reprargs = self.repr_args(entry) if not short else None
 
571
            s = self.get_source(source, line_index, excinfo, short=short)
 
572
            lines.extend(s)
 
573
            if short:
 
574
                message = "in %s" %(entry.name)
 
575
            else:
 
576
                message = excinfo and excinfo.typename or ""
 
577
            path = self._makepath(entry.path)
 
578
            filelocrepr = ReprFileLocation(path, entry.lineno+1, message)
 
579
            localsrepr = None
 
580
            if not short:
 
581
                localsrepr =  self.repr_locals(entry.locals)
 
582
            return ReprEntry(lines, reprargs, localsrepr, filelocrepr, style)
 
583
        if excinfo:
 
584
            lines.extend(self.get_exconly(excinfo, indent=4))
 
585
        return ReprEntry(lines, None, None, None, style)
 
586
 
 
587
    def _makepath(self, path):
 
588
        if not self.abspath:
 
589
            try:
 
590
                np = py.path.local().bestrelpath(path)
 
591
            except OSError:
 
592
                return path
 
593
            if len(np) < len(str(path)):
 
594
                path = np
 
595
        return path
 
596
 
 
597
    def repr_traceback(self, excinfo):
 
598
        traceback = excinfo.traceback
 
599
        if self.tbfilter:
 
600
            traceback = traceback.filter()
 
601
        recursionindex = None
 
602
        if is_recursion_error(excinfo):
 
603
            recursionindex = traceback.recursionindex()
 
604
        last = traceback[-1]
 
605
        entries = []
 
606
        extraline = None
 
607
        for index, entry in enumerate(traceback):
 
608
            einfo = (last == entry) and excinfo or None
 
609
            reprentry = self.repr_traceback_entry(entry, einfo)
 
610
            entries.append(reprentry)
 
611
            if index == recursionindex:
 
612
                extraline = "!!! Recursion detected (same locals & position)"
 
613
                break
 
614
        return ReprTraceback(entries, extraline, style=self.style)
 
615
 
 
616
 
 
617
    def repr_excinfo(self, excinfo):
 
618
        if sys.version_info[0] < 3:
 
619
            reprtraceback = self.repr_traceback(excinfo)
 
620
            reprcrash = excinfo._getreprcrash()
 
621
 
 
622
            return ReprExceptionInfo(reprtraceback, reprcrash)
 
623
        else:
 
624
            repr_chain = []
 
625
            e = excinfo.value
 
626
            descr = None
 
627
            while e is not None:
 
628
                if excinfo:
 
629
                    reprtraceback = self.repr_traceback(excinfo)
 
630
                    reprcrash = excinfo._getreprcrash()
 
631
                else:
 
632
                    # fallback to native repr if the exception doesn't have a traceback:
 
633
                    # ExceptionInfo objects require a full traceback to work
 
634
                    reprtraceback = ReprTracebackNative(py.std.traceback.format_exception(type(e), e, None))
 
635
                    reprcrash = None
 
636
 
 
637
                repr_chain += [(reprtraceback, reprcrash, descr)]
 
638
                if e.__cause__ is not None:
 
639
                    e = e.__cause__
 
640
                    excinfo = ExceptionInfo((type(e), e, e.__traceback__)) if e.__traceback__ else None
 
641
                    descr = 'The above exception was the direct cause of the following exception:'
 
642
                elif e.__context__ is not None:
 
643
                    e = e.__context__
 
644
                    excinfo = ExceptionInfo((type(e), e, e.__traceback__)) if e.__traceback__ else None
 
645
                    descr = 'During handling of the above exception, another exception occurred:'
 
646
                else:
 
647
                    e = None
 
648
            repr_chain.reverse()
 
649
            return ExceptionChainRepr(repr_chain)
 
650
 
 
651
 
 
652
class TerminalRepr(object):
 
653
    def __str__(self):
 
654
        s = self.__unicode__()
 
655
        if sys.version_info[0] < 3:
 
656
            s = s.encode('utf-8')
 
657
        return s
 
658
 
 
659
    def __unicode__(self):
 
660
        # FYI this is called from pytest-xdist's serialization of exception
 
661
        # information.
 
662
        io = py.io.TextIO()
 
663
        tw = py.io.TerminalWriter(file=io)
 
664
        self.toterminal(tw)
 
665
        return io.getvalue().strip()
 
666
 
 
667
    def __repr__(self):
 
668
        return "<%s instance at %0x>" %(self.__class__, id(self))
 
669
 
 
670
 
 
671
class ExceptionRepr(TerminalRepr):
 
672
    def __init__(self):
 
673
        self.sections = []
 
674
 
 
675
    def addsection(self, name, content, sep="-"):
 
676
        self.sections.append((name, content, sep))
 
677
 
 
678
    def toterminal(self, tw):
 
679
        for name, content, sep in self.sections:
 
680
            tw.sep(sep, name)
 
681
            tw.line(content)
 
682
 
 
683
 
 
684
class ExceptionChainRepr(ExceptionRepr):
 
685
    def __init__(self, chain):
 
686
        super(ExceptionChainRepr, self).__init__()
 
687
        self.chain = chain
 
688
        # reprcrash and reprtraceback of the outermost (the newest) exception
 
689
        # in the chain
 
690
        self.reprtraceback = chain[-1][0]
 
691
        self.reprcrash = chain[-1][1]
 
692
 
 
693
    def toterminal(self, tw):
 
694
        for element in self.chain:
 
695
            element[0].toterminal(tw)
 
696
            if element[2] is not None:
 
697
                tw.line("")
 
698
                tw.line(element[2], yellow=True)
 
699
        super(ExceptionChainRepr, self).toterminal(tw)
 
700
 
 
701
 
 
702
class ReprExceptionInfo(ExceptionRepr):
 
703
    def __init__(self, reprtraceback, reprcrash):
 
704
        super(ReprExceptionInfo, self).__init__()
 
705
        self.reprtraceback = reprtraceback
 
706
        self.reprcrash = reprcrash
 
707
 
 
708
    def toterminal(self, tw):
 
709
        self.reprtraceback.toterminal(tw)
 
710
        super(ReprExceptionInfo, self).toterminal(tw)
 
711
 
 
712
class ReprTraceback(TerminalRepr):
 
713
    entrysep = "_ "
 
714
 
 
715
    def __init__(self, reprentries, extraline, style):
 
716
        self.reprentries = reprentries
 
717
        self.extraline = extraline
 
718
        self.style = style
 
719
 
 
720
    def toterminal(self, tw):
 
721
        # the entries might have different styles
 
722
        for i, entry in enumerate(self.reprentries):
 
723
            if entry.style == "long":
 
724
                tw.line("")
 
725
            entry.toterminal(tw)
 
726
            if i < len(self.reprentries) - 1:
 
727
                next_entry = self.reprentries[i+1]
 
728
                if entry.style == "long" or \
 
729
                   entry.style == "short" and next_entry.style == "long":
 
730
                    tw.sep(self.entrysep)
 
731
 
 
732
        if self.extraline:
 
733
            tw.line(self.extraline)
 
734
 
 
735
class ReprTracebackNative(ReprTraceback):
 
736
    def __init__(self, tblines):
 
737
        self.style = "native"
 
738
        self.reprentries = [ReprEntryNative(tblines)]
 
739
        self.extraline = None
 
740
 
 
741
class ReprEntryNative(TerminalRepr):
 
742
    style = "native"
 
743
 
 
744
    def __init__(self, tblines):
 
745
        self.lines = tblines
 
746
 
 
747
    def toterminal(self, tw):
 
748
        tw.write("".join(self.lines))
 
749
 
 
750
class ReprEntry(TerminalRepr):
 
751
    localssep = "_ "
 
752
 
 
753
    def __init__(self, lines, reprfuncargs, reprlocals, filelocrepr, style):
 
754
        self.lines = lines
 
755
        self.reprfuncargs = reprfuncargs
 
756
        self.reprlocals = reprlocals
 
757
        self.reprfileloc = filelocrepr
 
758
        self.style = style
 
759
 
 
760
    def toterminal(self, tw):
 
761
        if self.style == "short":
 
762
            self.reprfileloc.toterminal(tw)
 
763
            for line in self.lines:
 
764
                red = line.startswith("E   ")
 
765
                tw.line(line, bold=True, red=red)
 
766
            #tw.line("")
 
767
            return
 
768
        if self.reprfuncargs:
 
769
            self.reprfuncargs.toterminal(tw)
 
770
        for line in self.lines:
 
771
            red = line.startswith("E   ")
 
772
            tw.line(line, bold=True, red=red)
 
773
        if self.reprlocals:
 
774
            #tw.sep(self.localssep, "Locals")
 
775
            tw.line("")
 
776
            self.reprlocals.toterminal(tw)
 
777
        if self.reprfileloc:
 
778
            if self.lines:
 
779
                tw.line("")
 
780
            self.reprfileloc.toterminal(tw)
 
781
 
 
782
    def __str__(self):
 
783
        return "%s\n%s\n%s" % ("\n".join(self.lines),
 
784
                               self.reprlocals,
 
785
                               self.reprfileloc)
 
786
 
 
787
class ReprFileLocation(TerminalRepr):
 
788
    def __init__(self, path, lineno, message):
 
789
        self.path = str(path)
 
790
        self.lineno = lineno
 
791
        self.message = message
 
792
 
 
793
    def toterminal(self, tw):
 
794
        # filename and lineno output for each entry,
 
795
        # using an output format that most editors unterstand
 
796
        msg = self.message
 
797
        i = msg.find("\n")
 
798
        if i != -1:
 
799
            msg = msg[:i]
 
800
        tw.write(self.path, bold=True, red=True)
 
801
        tw.line(":%s: %s" % (self.lineno, msg))
 
802
 
 
803
class ReprLocals(TerminalRepr):
 
804
    def __init__(self, lines):
 
805
        self.lines = lines
 
806
 
 
807
    def toterminal(self, tw):
 
808
        for line in self.lines:
 
809
            tw.line(line)
 
810
 
 
811
class ReprFuncArgs(TerminalRepr):
 
812
    def __init__(self, args):
 
813
        self.args = args
 
814
 
 
815
    def toterminal(self, tw):
 
816
        if self.args:
 
817
            linesofar = ""
 
818
            for name, value in self.args:
 
819
                ns = "%s = %s" %(name, value)
 
820
                if len(ns) + len(linesofar) + 2 > tw.fullwidth:
 
821
                    if linesofar:
 
822
                        tw.line(linesofar)
 
823
                    linesofar =  ns
 
824
                else:
 
825
                    if linesofar:
 
826
                        linesofar += ", " + ns
 
827
                    else:
 
828
                        linesofar = ns
 
829
            if linesofar:
 
830
                tw.line(linesofar)
 
831
            tw.line("")
 
832
 
 
833
 
 
834
def getrawcode(obj, trycall=True):
 
835
    """ return code object for given function. """
 
836
    try:
 
837
        return obj.__code__
 
838
    except AttributeError:
 
839
        obj = getattr(obj, 'im_func', obj)
 
840
        obj = getattr(obj, 'func_code', obj)
 
841
        obj = getattr(obj, 'f_code', obj)
 
842
        obj = getattr(obj, '__code__', obj)
 
843
        if trycall and not hasattr(obj, 'co_firstlineno'):
 
844
            if hasattr(obj, '__call__') and not py.std.inspect.isclass(obj):
 
845
                x = getrawcode(obj.__call__, trycall=False)
 
846
                if hasattr(x, 'co_firstlineno'):
 
847
                    return x
 
848
        return obj
 
849
 
 
850
 
 
851
if sys.version_info[:2] >= (3, 5):  # RecursionError introduced in 3.5
 
852
    def is_recursion_error(excinfo):
 
853
        return excinfo.errisinstance(RecursionError)  # noqa
 
854
else:
 
855
    def is_recursion_error(excinfo):
 
856
        if not excinfo.errisinstance(RuntimeError):
 
857
            return False
 
858
        try:
 
859
            return "maximum recursion depth exceeded" in str(excinfo.value)
 
860
        except UnicodeError:
 
861
            return False