Skip to content
Snippets Groups Projects
Commit 042bee3b authored by Piotr Maślanka's avatar Piotr Maślanka
Browse files

multiple fixes for Traceback:

* added __eq__ methods to Traceback classes
* extended Traceback to fetch current stack frame if no exception is in progress and no stack frame is given
* added a feature to load a Traceback from a stream
parent f5718e4b
No related branches found
No related tags found
No related merge requests found
......@@ -2,3 +2,5 @@
* fix exception hierarchy in futures
* extended Traceback to fetch current stack frame if no exception is in progress and no stack frame is given
* added a feature to load a Traceback from a stream
* added __eq__ methods to Traceback classes
......@@ -6,7 +6,7 @@ Traceback object is used to preserve all the information you can get
in case of an exception. It preserves:
* Information about all stack frames in this thread
* Every local and global variable at every stack frame
* If the variable is pickable, and pickling is enabled, it is pickled on the spot
* If the variable is picklable, and pickling is enabled, it is pickled on the spot
* Variable's _repr_ is always preserved
* Of course variable name is saved
......
__version__ = '2.11.26_a4'
__version__ = '2.11.26_a5'
......@@ -189,6 +189,9 @@ class StoredVariableValue(JSONAble):
raise ValueError(
'object picklable, but cannot load in this environment')
def __eq__(self, other: 'StoredVariableValue') -> bool:
return self.repr == other.repr and self.type_ == other.type_
class StackFrame(JSONAble):
"""
......@@ -209,6 +212,11 @@ class StackFrame(JSONAble):
for key, value in frame.f_globals.items():
self.globals[key] = StoredVariableValue(value, policy)
def __eq__(self, other: 'StackFrame') -> bool:
return self.name == other.name and self.filename == other.filename and \
self.lineno == other.lineno and self.locals == other.locals and \
self.globals == other.globals
@classmethod
def from_json(cls, x: dict) -> 'StackFrame':
sv = StackFrame.__new__(StackFrame)
......
......@@ -61,6 +61,9 @@ class Traceback(JSONAble):
self.formatted_traceback = str(traceback.format_exc())
def __eq__(self, other: 'Traceback') -> bool:
return self.frames == other.frames
def pickle_to(self, stream: tp.BinaryIO) -> None:
"""Pickle self to target stream"""
pickle.dump(self, stream, pickle.HIGHEST_PROTOCOL)
......@@ -71,6 +74,19 @@ class Traceback(JSONAble):
self.pickle_to(bio)
return bio.getvalue()
@classmethod
def from_pickle(cls, pick: tp.Union[io.BytesIO, bytes]) -> 'Traceback':
"""
Load a traceback from a pickle
:param pick: either bytes or a BytesIO to load it from
:return: previously serialized Traceback
"""
if isinstance(pick, io.BytesIO):
return pickle.load(pick)
else:
return pickle.loads(pick)
def to_json(self) -> dict:
return {
'frames': [frame.to_json() for frame in self.frames],
......
import io
import pickle
import sys
import unittest
......@@ -7,7 +8,15 @@ from satella.instrumentation import Traceback
class TestTraceback(unittest.TestCase):
def test_no_exc(self):
Traceback()
tb = Traceback()
byte = io.BytesIO()
byte2 = tb.pickle()
tb.pickle_to(byte)
byte.seek(0)
tb2 = Traceback.from_pickle(byte)
tb3 = Traceback.from_pickle(byte2)
self.assertEqual(tb, tb2)
self.assertEqual(tb2, tb3)
def test_json(self):
try:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment