Skip to content

Commit 0f8275a

Browse files
committed
Make einfo compatible with inspect (fixes #176)
1 parent b113d93 commit 0f8275a

File tree

2 files changed

+99
-1
lines changed

2 files changed

+99
-1
lines changed

billiard/einfo.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import sys
22
import traceback
3+
import types
34

45
__all__ = ['ExceptionInfo', 'Traceback']
56

@@ -26,6 +27,13 @@ def __init__(self, code):
2627
self.co_qualname = code.co_qualname
2728
self._co_positions = list(code.co_positions())
2829

30+
@property
31+
def __class__(self):
32+
return types.CodeType
33+
34+
def __reduce__(self):
35+
return _Code.__new__, (_Code,), self.__dict__
36+
2937
if sys.version_info >= (3, 11):
3038
@property
3139
def co_positions(self):
@@ -58,6 +66,13 @@ def __init__(self, frame):
5866
# don't want to hit https://bugs.python.org/issue21967
5967
self.f_restricted = False
6068

69+
@property
70+
def __class__(self):
71+
return types.FrameType
72+
73+
def __reduce__(self):
74+
return _Frame.__new__, (_Frame,), self.__dict__
75+
6176
if sys.version_info >= (3, 11):
6277
@property
6378
def co_positions(self):
@@ -100,6 +115,13 @@ def __init__(self):
100115
self.tb_next = None
101116
self.tb_lasti = 0
102117

118+
@property
119+
def __class__(self):
120+
return types.TracebackType
121+
122+
def __reduce__(self):
123+
return _Truncated.__new__, (_Truncated,), self.__dict__
124+
103125
if sys.version_info >= (3, 11):
104126
@property
105127
def co_positions(self):
@@ -120,6 +142,13 @@ def __init__(self, tb, max_frames=DEFAULT_MAX_FRAMES, depth=0):
120142
else:
121143
self.tb_next = _Truncated()
122144

145+
@property
146+
def __class__(self):
147+
return types.TracebackType
148+
149+
def __reduce__(self):
150+
return Traceback.__new__, (Traceback,), self.__dict__
151+
123152

124153
class RemoteTraceback(Exception):
125154
def __init__(self, tb):

t/unit/test_einfo.py

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import inspect
12
import logging
23
import pickle
34
import sys
5+
import types
46

57
from billiard.einfo import _Code # noqa
68
from billiard.einfo import _Frame # noqa
@@ -86,7 +88,7 @@ def test_code():
8688
assert isinstance(code.co_argcount, int)
8789
if sys.version_info >= (3, 11):
8890
assert callable(code.co_positions)
89-
assert next(code.co_positions()) == (77, 77, 0, 0)
91+
assert next(code.co_positions()) == (79, 79, 0, 0)
9092

9193

9294
def test_object_init():
@@ -116,3 +118,70 @@ def test_truncated_co_positions():
116118
assert list(iter(truncated.co_positions())) == list(
117119
iter(truncated.tb_frame.co_positions())
118120
)
121+
122+
123+
def make_python_tb():
124+
tb = None
125+
depth = 0
126+
while True:
127+
try:
128+
frame = sys._getframe(depth)
129+
except ValueError:
130+
break
131+
else:
132+
depth += 1
133+
134+
tb = types.TracebackType(tb, frame, frame.f_lasti, frame.f_lineno)
135+
136+
assert tb is not None, "Failed to create a traceback object"
137+
138+
return tb
139+
140+
141+
def test_isinstance():
142+
tb = Traceback(tb=make_python_tb())
143+
frame = tb.tb_frame
144+
code = frame.f_code
145+
146+
assert isinstance(tb, types.TracebackType)
147+
assert isinstance(tb, Traceback)
148+
assert isinstance(frame, types.FrameType)
149+
assert isinstance(frame, _Frame)
150+
assert isinstance(code, types.CodeType)
151+
assert isinstance(code, _Code)
152+
153+
154+
def repickle(obj):
155+
"""Round-trip an object through pickle."""
156+
return pickle.loads(pickle.dumps(obj))
157+
158+
159+
class test_inspect:
160+
def test_istraceback(self):
161+
tb = Traceback(tb=make_python_tb())
162+
assert inspect.istraceback(tb)
163+
assert inspect.istraceback(repickle(tb))
164+
165+
def test_isframe(self):
166+
frame = _Frame(make_python_tb().tb_frame)
167+
assert inspect.isframe(frame)
168+
assert inspect.isframe(repickle(frame))
169+
170+
def test_iscode(self):
171+
code = _Code(make_python_tb().tb_frame.f_code)
172+
assert inspect.iscode(code)
173+
assert inspect.iscode(repickle(code))
174+
175+
def test_getframeinfo(self):
176+
tb = Traceback(make_python_tb())
177+
assert inspect.getframeinfo(tb)
178+
assert inspect.getframeinfo(repickle(tb))
179+
180+
frame = tb.tb_frame
181+
assert inspect.getframeinfo(frame)
182+
assert inspect.getframeinfo(repickle(frame))
183+
184+
def test_getinnerframes(self):
185+
tb = Traceback(tb=make_python_tb())
186+
assert inspect.getinnerframes(tb)
187+
assert inspect.getinnerframes(repickle(tb))

0 commit comments

Comments
 (0)