Ensure that failures can be pickled
When a failure happens in a subprocess it needs to be pickleable (and not contain a traceback, since those can not be pickled) when being sent across the process boundary so that the receiving process can read it and unpickle the corresponding object. Part of blueprint process-executor Change-Id: I2f26faa4e02da6acf4f0840239d0b17143de8d76
This commit is contained in:
@@ -34,6 +34,23 @@ def _copy_exc_info(exc_info):
|
||||
return (exc_type, copy.copy(exc_value), tb)
|
||||
|
||||
|
||||
def _fill_iter(it, desired_len, filler=None):
|
||||
"""Iterates over a provided iterator up to the desired length.
|
||||
|
||||
If the source iterator does not have enough values then the filler
|
||||
value is yielded until the desired length is reached.
|
||||
"""
|
||||
count = 0
|
||||
for value in it:
|
||||
if count >= desired_len:
|
||||
return
|
||||
yield value
|
||||
count += 1
|
||||
while count < desired_len:
|
||||
yield filler
|
||||
count += 1
|
||||
|
||||
|
||||
def _are_equal_exc_info_tuples(ei1, ei2):
|
||||
if ei1 == ei2:
|
||||
return True
|
||||
@@ -274,6 +291,30 @@ class Failure(object):
|
||||
for et in self._exc_type_names:
|
||||
yield et
|
||||
|
||||
def __getstate__(self):
|
||||
dct = self.to_dict()
|
||||
if self._exc_info:
|
||||
# Avoids 'TypeError: can't pickle traceback objects'
|
||||
dct['exc_info'] = self._exc_info[0:2]
|
||||
return dct
|
||||
|
||||
def __setstate__(self, dct):
|
||||
self._exception_str = dct['exception_str']
|
||||
self._traceback_str = dct['traceback_str']
|
||||
self._exc_type_names = dct['exc_type_names']
|
||||
if 'exc_info' in dct:
|
||||
# Tracebacks can't be serialized/deserialized, but since we
|
||||
# provide a traceback string (and more) this should be
|
||||
# acceptable...
|
||||
#
|
||||
# TODO(harlowja): in the future we could do something like
|
||||
# what the twisted people have done, see for example
|
||||
# twisted-13.0.0/twisted/python/failure.py#L89 for how they
|
||||
# created a fake traceback object...
|
||||
self._exc_info = tuple(_fill_iter(dct['exc_info'], 3))
|
||||
else:
|
||||
self._exc_info = None
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data):
|
||||
"""Converts this from a dictionary to a object."""
|
||||
|
||||
Reference in New Issue
Block a user