TracebackParseError   A
last analyzed

Complexity

Total Complexity 0

Size/Duplication

Total Lines 2
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 0
c 0
b 0
f 0
dl 0
loc 2
rs 10
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.2'
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 = {
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
    def clear(self):
55
        # For compatibility with PyPy 3.5;
56
        # clear() was added to frame in Python 3.4
57
        # and is called by traceback.clear_frames(), which
58
        # in turn is called by unittest.TestCase.assertRaises
59
        pass
60
61
62
class Traceback(object):
63
64
    tb_next = None
65
66
    def __init__(self, tb):
67
        self.tb_frame = Frame(tb.tb_frame)
68
        # noinspection SpellCheckingInspection
69
        self.tb_lineno = int(tb.tb_lineno)
70
71
        # Build in place to avoid exceeding the recursion limit
72
        tb = tb.tb_next
73
        prev_traceback = self
74
        cls = type(self)
75
        while tb is not None:
76
            traceback = object.__new__(cls)
77
            traceback.tb_frame = Frame(tb.tb_frame)
78
            traceback.tb_lineno = int(tb.tb_lineno)
79
            prev_traceback.tb_next = traceback
80
            prev_traceback = traceback
81
            tb = tb.tb_next
82
83
    def as_traceback(self):
84
        if tproxy:
85
            return tproxy(TracebackType, self.__tproxy_handler)
86
        if not tb_set_next:
87
            raise RuntimeError("Unsupported Python interpreter!")
88
89
        current = self
90
        top_tb = None
91
        tb = None
92
        while current:
93
            f_code = current.tb_frame.f_code
94
            code = compile('\n' * (current.tb_lineno - 1) + 'raise __traceback_maker', current.tb_frame.f_code.co_filename, 'exec')
95
            if PY3:
96
                code = CodeType(
97
                    0, code.co_kwonlyargcount,
98
                    code.co_nlocals, code.co_stacksize, code.co_flags,
99
                    code.co_code, code.co_consts, code.co_names, code.co_varnames,
100
                    f_code.co_filename, f_code.co_name,
101
                    code.co_firstlineno, code.co_lnotab, (), ()
102
                )
103
            else:
104
                code = CodeType(
105
                    0,
106
                    code.co_nlocals, code.co_stacksize, code.co_flags,
107
                    code.co_code, code.co_consts, code.co_names, code.co_varnames,
108
                    f_code.co_filename.encode(), f_code.co_name.encode(),
109
                    code.co_firstlineno, code.co_lnotab, (), ()
110
                )
111
112
            # noinspection PyBroadException
113
            try:
114
                exec(code, current.tb_frame.f_globals, {})
115
            except Exception:
116
                next_tb = sys.exc_info()[2].tb_next
117
                if top_tb is None:
118
                    top_tb = next_tb
119
                if tb is not None:
120
                    tb_set_next(tb, next_tb)
121
                tb = next_tb
122
                del next_tb
123
124
            current = current.tb_next
125
        try:
126
            return top_tb
127
        finally:
128
            del top_tb
129
            del tb
130
131
    # noinspection SpellCheckingInspection
132
    def __tproxy_handler(self, operation, *args, **kwargs):
133
        if operation in ('__getattribute__', '__getattr__'):
134
            if args[0] == 'tb_next':
135
                return self.tb_next and self.tb_next.as_traceback()
136
            else:
137
                return getattr(self, args[0])
138
        else:
139
            return getattr(self, operation)(*args, **kwargs)
140
141
    def to_dict(self):
142
        """Convert a Traceback into a dictionary representation"""
143
        if self.tb_next is None:
144
            tb_next = None
145
        else:
146
            tb_next = self.tb_next.to_dict()
147
148
        code = {
149
            'co_filename': self.tb_frame.f_code.co_filename,
150
            'co_name': self.tb_frame.f_code.co_name,
151
        }
152
        frame = {
153
            'f_globals': self.tb_frame.f_globals,
154
            'f_code': code,
155
        }
156
        return {
157
            'tb_frame': frame,
158
            'tb_lineno': self.tb_lineno,
159
            'tb_next': tb_next,
160
        }
161
162
    @classmethod
163
    def from_dict(cls, dct):
164
        if dct['tb_next']:
165
            tb_next = cls.from_dict(dct['tb_next'])
166
        else:
167
            tb_next = None
168
169
        code = _AttrDict(
170
            co_filename=dct['tb_frame']['f_code']['co_filename'],
171
            co_name=dct['tb_frame']['f_code']['co_name'],
172
        )
173
        frame = _AttrDict(
174
            f_globals=dct['tb_frame']['f_globals'],
175
            f_code=code,
176
        )
177
        tb = _AttrDict(
178
            tb_frame=frame,
179
            tb_lineno=dct['tb_lineno'],
180
            tb_next=tb_next,
181
        )
182
        return cls(tb)
183
184
    @classmethod
185
    def from_string(cls, string, strict=True):
186
        frames = []
187
        header = strict
188
189
        for line in string.splitlines():
190
            line = line.rstrip()
191
            if header:
192
                if line == 'Traceback (most recent call last):':
193
                    header = False
194
                continue
195
            frame_match = FRAME_RE.match(line)
196
            if frame_match:
197
                frames.append(frame_match.groupdict())
198
            elif line.startswith('  '):
199
                pass
200
            elif strict:
201
                break  # traceback ended
202
203
        if frames:
204
            previous = None
205
            for frame in reversed(frames):
206
                previous = _AttrDict(
207
                    frame,
208
                    tb_frame=_AttrDict(
209
                        frame,
210
                        f_globals=_AttrDict(
211
                            __file__=frame['co_filename'],
212
                            __name__='?',
213
                        ),
214
                        f_code=_AttrDict(frame),
215
                    ),
216
                    tb_next=previous,
217
                )
218
            return cls(previous)
219
        else:
220
            raise TracebackParseError("Could not find any frames in %r." % string)
221