Traceback fiddling library.
Project description
Traceback fiddling library.
Pickling tracebacks
Note: The traceback objects that come out are stripped of some attributes (like variables). But you’ll be able to raise exceptions with those tracebacks or print them - that should cover 99% of the usecases.
>>> from tblib import pickling_support
>>> pickling_support.install()
>>> import pickle, sys
>>> def inner_0():
... raise Exception('fail')
...
>>> def inner_1():
... inner_0()
...
>>> def inner_2():
... inner_1()
...
>>> try:
... inner_2()
... except:
... s1 = pickle.dumps(sys.exc_info())
...
>>> len(s1) > 1
True
>>> try:
... inner_2()
... except:
... s2 = pickle.dumps(sys.exc_info(), protocol=pickle.HIGHEST_PROTOCOL)
...
>>> len(s2) > 1
True
>>> try:
... import cPickle
... except ImportError:
... import pickle as cPickle
>>> try:
... inner_2()
... except:
... s3 = cPickle.dumps(sys.exc_info(), protocol=pickle.HIGHEST_PROTOCOL)
...
>>> len(s3) > 1
True
Unpickling
>>> pickle.loads(s1)
(<...Exception'>, Exception('fail',), <traceback object at ...>)
>>> pickle.loads(s2)
(<...Exception'>, Exception('fail',), <traceback object at ...>)
>>> pickle.loads(s3)
(<...Exception'>, Exception('fail',), <traceback object at ...>)
Raising
>>> from six import reraise
>>> reraise(*pickle.loads(s1))
Traceback (most recent call last):
...
File "<doctest README.rst[14]>", line 1, in <module>
reraise(*pickle.loads(s2))
File "<doctest README.rst[8]>", line 2, in <module>
inner_2()
File "<doctest README.rst[5]>", line 2, in inner_2
inner_1()
File "<doctest README.rst[4]>", line 2, in inner_1
inner_0()
File "<doctest README.rst[3]>", line 2, in inner_0
raise Exception('fail')
Exception: fail
>>> reraise(*pickle.loads(s2))
Traceback (most recent call last):
...
File "<doctest README.rst[14]>", line 1, in <module>
reraise(*pickle.loads(s2))
File "<doctest README.rst[8]>", line 2, in <module>
inner_2()
File "<doctest README.rst[5]>", line 2, in inner_2
inner_1()
File "<doctest README.rst[4]>", line 2, in inner_1
inner_0()
File "<doctest README.rst[3]>", line 2, in inner_0
raise Exception('fail')
Exception: fail
>>> reraise(*pickle.loads(s3))
Traceback (most recent call last):
...
File "<doctest README.rst[14]>", line 1, in <module>
reraise(*pickle.loads(s2))
File "<doctest README.rst[8]>", line 2, in <module>
inner_2()
File "<doctest README.rst[5]>", line 2, in inner_2
inner_1()
File "<doctest README.rst[4]>", line 2, in inner_1
inner_0()
File "<doctest README.rst[3]>", line 2, in inner_0
raise Exception('fail')
Exception: fail
The tblib.Traceback object
It is used by the pickling_support. You can use it too if you want more flexibility:
>>> from tblib import Traceback
>>> try:
... inner_2()
... except:
... et, ev, tb = sys.exc_info()
... tb = Traceback(tb)
... reraise(et, ev, tb.as_traceback())
...
Traceback (most recent call last):
...
File "<doctest README.rst[21]>", line 6, in <module>
reraise(et, ev, tb.as_traceback())
File "<doctest README.rst[21]>", line 2, in <module>
inner_2()
File "<doctest README.rst[5]>", line 2, in inner_2
inner_1()
File "<doctest README.rst[4]>", line 2, in inner_1
inner_0()
File "<doctest README.rst[3]>", line 2, in inner_0
raise Exception('fail')
Exception: fail
Decorators
return_error
>>> from tblib.decorators import return_error
>>> inner_2r = return_error(inner_2)
>>> e = inner_2r()
>>> e
<tblib.decorators.Error object at ...>
>>> e.reraise()
Traceback (most recent call last):
...
File "<doctest README.rst[26]>", line 1, in <module>
e.reraise()
File ".../tblib/decorators.py", line 19, in reraise
reraise(self.exc_type, self.exc_value, self.traceback)
File ".../tblib/decorators.py", line 25, in return_exceptions_wrapper
return func(*args, **kwargs)
File "<doctest README.rst[5]>", line 2, in inner_2
inner_1()
File "<doctest README.rst[4]>", line 2, in inner_1
inner_0()
File "<doctest README.rst[3]>", line 2, in inner_0
raise Exception('fail')
Exception: fail
How’s this useful ? Imagine you’re using multiprocessing like this:
>>> import traceback
>>> from multiprocessing import Pool
>>> from examples import func_a
>>> pool = Pool()
>>> try:
... for i in pool.map(func_a, range(5)):
... print(i)
... except:
... print(traceback.format_exc())
...
Traceback (most recent call last):
File "<doctest README.rst[31]>", line 2, in <module>
for i in pool.map(func_a, range(5)):
File "/usr/lib/.../multiprocessing/pool.py", line ..., in map
return self.map_async(func, iterable, chunksize).get()
File "/usr/lib/.../multiprocessing/pool.py", line ..., in get
raise self._value
Exception: Guessing time !
<BLANKLINE>
>>> pool.terminate()
Not very useful is it? Let’s sort this out:
>>> from tblib.decorators import apply_with_return_error, Error
>>> from itertools import repeat
>>> pool = Pool()
>>> try:
... for i in pool.map(apply_with_return_error, zip(repeat(func_a), range(5))):
... if isinstance(i, Error):
... i.reraise()
... else:
... print(i)
... except:
... print(traceback.format_exc())
...
Traceback (most recent call last):
File "<doctest README.rst[36]>", line 4, in <module>
i.reraise()
File ".../tblib/decorators.py", line ..., in reraise
reraise(self.exc_type, self.exc_value, self.traceback)
File ".../tblib/decorators.py", line ..., in return_exceptions_wrapper
return func(*args, **kwargs)
File ".../tblib/decorators.py", line ..., in apply_with_return_error
return args[0](*args[1:])
File ".../examples.py", line 2, in func_a
func_b()
File ".../examples.py", line 5, in func_b
func_c()
File ".../examples.py", line 8, in func_c
func_d()
File ".../examples.py", line 11, in func_d
raise Exception("Guessing time !")
Exception: Guessing time !
<BLANKLINE>
>>> pool.terminate()