Ответ 1
dill
имеет некоторые хорошие диагностические инструменты для травления, лучшим из которых является трасса рассола (аналогичная тому, что вы реализовали).
Позвольте построить сложный объект и изучить:
>>> import dill
>>> class Foo(object):
... @classmethod
... def bar(self, x):
... return self.z + x
... def baz(self, z):
... self.z = z
... z = 1
... zap = lambda self, x: x + self.bar(x)
...
>>> f = Foo()
>>> f.zap(3)
7
>>> f.baz(7)
>>> f.z
7
Включить трассировку рассола:
>>> dill.detect.trace(True)
>>> _f = dill.dumps(f)
T2: <class '__main__.Foo'>
F2: <function _create_type at 0x10f94a668>
T1: <type 'type'>
F2: <function _load_type at 0x10f94a5f0>
T1: <type 'object'>
D2: <dict object at 0x10f96bb40>
Cm: <classmethod object at 0x10f9ad408>
T4: <type 'classmethod'>
F1: <function bar at 0x10f9aa9b0>
F2: <function _create_function at 0x10f94a6e0>
Co: <code object bar at 0x10f9a9130, file "<stdin>", line 2>
F2: <function _unmarshal at 0x10f94a578>
D1: <dict object at 0x10e8d6168>
D2: <dict object at 0x10f96b5c8>
F1: <function baz at 0x10f9aaa28>
Co: <code object baz at 0x10f9a9ab0, file "<stdin>", line 5>
D1: <dict object at 0x10e8d6168>
D2: <dict object at 0x10f969d70>
F1: <function <lambda> at 0x10f9aaaa0>
Co: <code object <lambda> at 0x10f9a9c30, file "<stdin>", line 8>
D1: <dict object at 0x10e8d6168>
D2: <dict object at 0x10f97d050>
D2: <dict object at 0x10e97b4b0>
>>> f_ = dill.loads(_f)
>>> f_.z
7
Хорошо, dill
может разжечь этот объект... так что сделайте его сложнее.
Сначала отключите трассировку.
>>> dill.detect.trace(False)
>>>
>>> f.y = xrange(5)
>>> f.w = iter([1,2,3])
>>>
>>> dill.pickles(f)
False
Хорошо, теперь dill
завершается сбой. Так что же является причиной неудачи?
Мы можем посмотреть на все объекты, которые не могут рассосаться, если мы копаем в наш объект f
.
>>> dill.detect.badtypes(f)
<class '__main__.Foo'>
>>> dill.detect.badtypes(f, depth=1)
{'__hash__': <type 'method-wrapper'>, '__setattr__': <type 'method-wrapper'>, '__reduce_ex__': <type 'builtin_function_or_method'>, 'baz': <type 'instancemethod'>, '__reduce__': <type 'builtin_function_or_method'>, '__str__': <type 'method-wrapper'>, '__format__': <type 'builtin_function_or_method'>, '__getattribute__': <type 'method-wrapper'>, 'zap': <type 'instancemethod'>, '__delattr__': <type 'method-wrapper'>, '__repr__': <type 'method-wrapper'>, 'w': <type 'listiterator'>, '__dict__': <type 'dict'>, '__sizeof__': <type 'builtin_function_or_method'>, '__init__': <type 'method-wrapper'>}
>>> dill.detect.badobjects(f, depth=1)
{'__hash__': <method-wrapper '__hash__' of Foo object at 0x10f9b0050>, '__setattr__': <method-wrapper '__setattr__' of Foo object at 0x10f9b0050>, '__reduce_ex__': <built-in method __reduce_ex__ of Foo object at 0x10f9b0050>, 'baz': <bound method Foo.baz of <__main__.Foo object at 0x10f9b0050>>, '__reduce__': <built-in method __reduce__ of Foo object at 0x10f9b0050>, '__str__': <method-wrapper '__str__' of Foo object at 0x10f9b0050>, '__format__': <built-in method __format__ of Foo object at 0x10f9b0050>, '__getattribute__': <method-wrapper '__getattribute__' of Foo object at 0x10f9b0050>, 'zap': <bound method Foo.<lambda> of <__main__.Foo object at 0x10f9b0050>>, '__delattr__': <method-wrapper '__delattr__' of Foo object at 0x10f9b0050>, '__repr__': <method-wrapper '__repr__' of Foo object at 0x10f9b0050>, 'w': <listiterator object at 0x10f9b0550>, '__dict__': {'y': xrange(5), 'z': 7, 'w': <listiterator object at 0x10f9b0550>}, '__sizeof__': <built-in method __sizeof__ of Foo object at 0x10f9b0050>, '__init__': <method-wrapper '__init__' of Foo object at 0x10f9b0050>}
Хммм. Это много. Конечно, не все эти объекты должны сериализоваться для нашего объекта для сериализации... однако по крайней мере один из них вызывает сбой.
Естественное дело - посмотреть на неудачу, которую мы получаем... Итак, какая ошибка, которая будет выброшена? Может быть, это даст подсказку.
>>> dill.detect.errors(f)
PicklingError("Can't pickle <type 'listiterator'>: it not found as __builtin__.listiterator",)
Aha, listiterator
- плохой объект. Позвольте копать глубже, снова включив "трассировку".
>>> dill.detect.trace(True)
>>> dill.pickles(f)
T2: <class '__main__.Foo'>
F2: <function _create_type at 0x10f94a668>
T1: <type 'type'>
F2: <function _load_type at 0x10f94a5f0>
T1: <type 'object'>
D2: <dict object at 0x10f9826e0>
Cm: <classmethod object at 0x10f9ad408>
T4: <type 'classmethod'>
F1: <function bar at 0x10f9aa9b0>
F2: <function _create_function at 0x10f94a6e0>
Co: <code object bar at 0x10f9a9130, file "<stdin>", line 2>
F2: <function _unmarshal at 0x10f94a578>
D1: <dict object at 0x10e8d6168>
D2: <dict object at 0x10f96b5c8>
F1: <function baz at 0x10f9aaa28>
Co: <code object baz at 0x10f9a9ab0, file "<stdin>", line 5>
D1: <dict object at 0x10e8d6168>
D2: <dict object at 0x10f969d70>
F1: <function <lambda> at 0x10f9aaaa0>
Co: <code object <lambda> at 0x10f9a9c30, file "<stdin>", line 8>
D1: <dict object at 0x10e8d6168>
D2: <dict object at 0x10f97d050>
D2: <dict object at 0x10e97b4b0>
Si: xrange(5)
F2: <function _eval_repr at 0x10f94acf8>
T4: <type 'listiterator'>
False
Действительно, он останавливается на listiterator
. Однако заметьте (только что выше), что xrange
делает рассол. Итак, заменим iter
на xrange
>>> f.w = xrange(1,4)
>>> dill.detect.trace(False)
>>> dill.pickles(f)
True
>>>
Наш объект снова рассоривается.
dill
содержит кучу других инструментов обнаружения рассола, в том числе методы отслеживания того, на какие объекты указывает (полезно для отладки рекурсивных травильных сбоев).
Я считаю, что cloudpickle
также имеет некоторые аналогичные инструменты для dill
для отладки pickle... но основной инструмент в любом случае похож на то, что вы создали.