Completed
Push — master ( 3c1059...41ee64 )
by Ionel Cristian
01:09
created

Traceback.as_traceback()   B

Complexity

Conditions 5

Size

Total Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 5
Bugs 0 Features 0
Metric Value
cc 5
c 5
b 0
f 0
dl 0
loc 32
rs 8.0894
1
import re
2
import sys
3
from types import CodeType
4
from types import TracebackType
5
6
try:
7
    from __pypy__ import tproxy
8
except ImportError:
9
    tproxy = None
10
try:
11
    from .cpython import tb_set_next
12
except ImportError:
13
    tb_set_next = None
14
15
if not tb_set_next and not tproxy:
16
    raise ImportError("Cannot use tblib. Runtime not supported.")
17
18
__version__ = '1.3.0'
19
__all__ = 'Traceback',
20
21
PY3 = sys.version_info[0] == 3
22
FRAME_RE = re.compile(r'^\s*File "(?P<co_filename>.+)", line (?P<tb_lineno>\d+)(, in (?P<co_name>.+))?$')
23
24
25
class _AttrDict(dict):
26
    __slots__ = ()
27
    __getattr__ = dict.__getitem__
28
29
30
# noinspection PyPep8Naming
31
class __traceback_maker(Exception):
32
    pass
33
34
35
class TracebackParseError(Exception):
36
    pass
37
38
39
class Code(object):
40
    def __init__(self, code):
41
        self.co_filename = code.co_filename
42
        self.co_name = code.co_name
43
44
45
class Frame(object):
46
    def __init__(self, frame):
47
        self.f_globals = dict([
48
            (k, v)
49
            for k, v in frame.f_globals.items()
50
            if k in ("__file__", "__name__")
51
        ])
52
        self.f_code = Code(frame.f_code)
53
54
55
class Traceback(object):
56
    def __init__(self, tb):
57
        self.tb_frame = Frame(tb.tb_frame)
58
        # noinspection SpellCheckingInspection
59
        self.tb_lineno = int(tb.tb_lineno)
60
        if tb.tb_next is None:
61
            self.tb_next = None
62
        else:
63
            self.tb_next = Traceback(tb.tb_next)
64
65
    def as_traceback(self):
66
        if tproxy:
67
            return tproxy(TracebackType, self.__tproxy_handler)
68
        elif tb_set_next:
69
            f_code = self.tb_frame.f_code
70
            code = compile('\n' * (self.tb_lineno - 1) + 'raise __traceback_maker', self.tb_frame.f_code.co_filename, 'exec')
71
            if PY3:
72
                code = CodeType(
73
                    0, code.co_kwonlyargcount,
74
                    code.co_nlocals, code.co_stacksize, code.co_flags,
75
                    code.co_code, code.co_consts, code.co_names, code.co_varnames,
76
                    f_code.co_filename, f_code.co_name,
77
                    code.co_firstlineno, code.co_lnotab, (), ()
78
                )
79
            else:
80
                code = CodeType(
81
                    0,
82
                    code.co_nlocals, code.co_stacksize, code.co_flags,
83
                    code.co_code, code.co_consts, code.co_names, code.co_varnames,
84
                    f_code.co_filename.encode(), f_code.co_name.encode(),
85
                    code.co_firstlineno, code.co_lnotab, (), ()
86
                )
87
88
            # noinspection PyBroadException
89
            try:
90
                exec(code, self.tb_frame.f_globals, {})
91
            except:
92
                tb = sys.exc_info()[2].tb_next
93
                tb_set_next(tb, self.tb_next and self.tb_next.as_traceback())
94
                return tb
95
        else:
96
            raise RuntimeError("Cannot re-create traceback !")
97
98
    # noinspection SpellCheckingInspection
99
    def __tproxy_handler(self, operation, *args, **kwargs):
100
        if operation in ('__getattribute__', '__getattr__'):
101
            if args[0] == 'tb_next':
102
                return self.tb_next and self.tb_next.as_traceback()
103
            else:
104
                return getattr(self, args[0])
105
        else:
106
            return getattr(self, operation)(*args, **kwargs)
107
108
    def to_dict(self):
109
        """Convert a Traceback into a dictionary representation"""
110
        if self.tb_next is None:
111
            tb_next = None
112
        else:
113
            tb_next = self.tb_next.to_dict()
114
115
        code = {
116
            'co_filename': self.tb_frame.f_code.co_filename,
117
            'co_name': self.tb_frame.f_code.co_name,
118
        }
119
        frame = {
120
            'f_globals': self.tb_frame.f_globals,
121
            'f_code': code,
122
        }
123
        return {
124
            'tb_frame': frame,
125
            'tb_lineno': self.tb_lineno,
126
            'tb_next': tb_next,
127
        }
128
129
    @classmethod
130
    def from_dict(cls, dct):
131
        if dct['tb_next']:
132
            tb_next = cls.from_dict(dct['tb_next'])
133
        else:
134
            tb_next = None
135
136
        code = _AttrDict(
137
            co_filename=dct['tb_frame']['f_code']['co_filename'],
138
            co_name=dct['tb_frame']['f_code']['co_name'],
139
        )
140
        frame = _AttrDict(
141
            f_globals=dct['tb_frame']['f_globals'],
142
            f_code=code,
143
        )
144
        tb = _AttrDict(
145
            tb_frame=frame,
146
            tb_lineno=dct['tb_lineno'],
147
            tb_next=tb_next,
148
        )
149
        return cls(tb)
150
151
    @classmethod
152
    def from_string(cls, string, strict=True):
153
        frames = []
154
        header = strict
155
156
        for line in string.splitlines():
157
            line = line.rstrip()
158
            if header:
159
                if line == 'Traceback (most recent call last):':
160
                    header = False
161
                continue
162
            frame_match = FRAME_RE.match(line)
163
            if frame_match:
164
                frames.append(frame_match.groupdict())
165
            elif line.startswith('  '):
166
                pass
167
            elif strict:
168
                break  # traceback ended
169
170
        if frames:
171
            previous = None
172
            for frame in reversed(frames):
173
                previous = _AttrDict(
174
                    frame,
175
                    tb_frame=_AttrDict(
176
                        frame,
177
                        f_globals=_AttrDict(
178
                            __file__=frame['co_filename'],
179
                            __name__='?',
180
                        ),
181
                        f_code=_AttrDict(frame),
182
                    ),
183
                    tb_next=previous,
184
                )
185
            return cls(previous)
186
        else:
187
            raise TracebackParseError("Could not find any frames in %r." % string)
188