2009-02-20 30 views
30

Cuando PIckle un objeto que tiene algunos atributos que no puede ser en escabeche se producirá un error con un mensaje de error genérico como:¿Cómo saber para qué objeto pickle de atributo falla?

PicklingError: Can't pickle <type 'instancemethod'>: attribute lookup __builtin__.instancemethod failed 

¿Hay alguna manera de saber qué atributo provocó la excepción? Estoy usando Python 2.5.2.

Aunque en principio entiendo la causa raíz del problema (por ejemplo, en el ejemplo anterior que tiene un método de instancia) todavía puede ser muy difícil señalarlo exactamente. En mi caso, ya definí un método personalizado __getstate__, pero me olvidé de un atributo crítico. Esto sucedió en una estructura complicada de objetos anidados, por lo que me tomó un tiempo identificar el atributo malo.

a lo solicitado, aquí es un ejemplo sencillo de la salmuera fueron intencionalmente falla:

import cPickle as pickle 
import new 

class Test(object): 
    pass 

def test_func(self): 
    pass 

test = Test() 
pickle.dumps(test) 
print "now with instancemethod..." 
test.test_meth = new.instancemethod(test_func, test) 
pickle.dumps(test) 

Ésta es la salida:

now with instancemethod... 
Traceback (most recent call last): 
    File "/home/wilbert/develop/workspace/Playground/src/misc/picklefail.py", line 15, in <module> 
    pickle.dumps(test) 
    File "/home/wilbert/lib/python2.5/copy_reg.py", line 69, in _reduce_ex 
    raise TypeError, "can't pickle %s objects" % base.__name__ 
TypeError: can't pickle instancemethod objects 

Desafortunadamente no hay ningún indicio de que el atributo test_meth causa el problema.

+0

puede que tal vez dar un pequeño ejemplo de un atributo no? o al menos, mostrar un poco más del rastreo para ver en qué parte del módulo de pickle falla? – MrTopf

+0

oh, ¿y qué versión de Python estás usando? – MrTopf

+0

@MrTopf: agregué la información – nikow

Respuesta

14

Puede presentar un error en Python por no incluir mensajes de error más útiles. Mientras tanto, modifique la función _reduce_ex() en copy_reg.py.

if base is self.__class__: 
    print self # new 
    raise TypeError, "can't pickle %s objects" % base.__name__ 

Salida:

<bound method ?.test_func of <__main__.Test object at 0xb7f4230c>> 
Traceback (most recent call last): 
    File "nopickle.py", line 14, in ? 
    pickle.dumps(test) 
    File "/usr/lib/python2.4/copy_reg.py", line 69, in _reduce_ex 
    raise TypeError, "can't pickle %s objects" % base.__name__ 
TypeError: can't pickle instancemethod objects 
+3

¿por qué no simplemente poner "self" en el mensaje de error en lugar de la impresión? – MrTopf

+1

Creo que esto cubre la excepción "TypeError" en el ejemplo; sin embargo, no se trata la excepción original "PicklingError". – cmcginty

+0

TypeError causa PicklingError. Una vez que dejas de intentar eliminar los objetos de método de instancia (o cualquier otro objeto no elegible) todo debería funcionar. – joeforker

8

tuve el mismo problema que tú, pero mis clases eran un poco más complicado (es decir, un gran árbol de objetos similares) por lo que la impresión no ayuda mucho por lo Hackeé juntos una función auxiliar. No está completo y solo está diseñado para su uso con el protocolo de decapado 2: Fue suficiente para poder localizar mis problemas. Si desea extenderlo para que cubra todo, el protocolo se describe en http://www.python.org/dev/peps/pep-0307/. He hecho que esta publicación sea editable para que todos puedan actualizar el código.

import pickle 
def get_pickling_errors(obj,seen=None): 
    if seen == None: 
     seen = [] 
    try: 
     state = obj.__getstate__() 
    except AttributeError: 
     return 
    if state == None: 
     return 
    if isinstance(state,tuple): 
     if not isinstance(state[0],dict): 
      state=state[1] 
     else: 
      state=state[0].update(state[1]) 
    result = {}  
    for i in state: 
     try: 
      pickle.dumps(state[i],protocol=2) 
     except pickle.PicklingError: 
      if not state[i] in seen: 
       seen.append(state[i]) 
       result[i]=get_pickling_errors(state[i],seen) 
    return result 

Un ejemplo del uso donde K es el objeto que no decapar

>>> get_pickling_errors(K) 
{'_gen': {}, '_base': {'_gens': None}} 

Esto significa que el attibute K._gen no está estibables y lo mismo pasa para K._base._gens .

+0

Gracias, ayuda. Y creo que '__getstate __()' debería definirse en la clase correspondiente. –

3

He encontrado que si subclase Pickler y envolver el método Pickler.save() en un intento, a excepción del bloque

import pickle 
class MyPickler (pickle.Pickler): 
    def save(self, obj): 
     try: 
      pickle.Pickler.save(self, obj) 
     except Exception, e: 
      import pdb;pdb.set_trace() 

Entonces llaman al igual que

import StringIO 
output = StringIO.StringIO() 
MyPickler(output).dump(thingee) 
+0

¿No es esto simplemente iniciar un depurador? ¿Hay alguna diferencia, por ejemplo, al usar el depurador PyDev de Eclipse para analizar el problema? – nikow

+0

Si el depurador PyDev se iniciará en la excepción y lo pondrá en el nivel correcto de ejecución, será el mismo. Aunque no uso un IDE. –

+0

Lo usé para localizar un error Pickle particularmente difícil de encontrar. Pero en lugar de tener la cláusula except para iniciar el depurador, solo hice que imprima obj y que se produzca la excepción. Luego, ejecutar MyPickler (output) .dump (obj) dio como resultado un bonito informe parecido a un traceback de exactamente dónde estaba el objeto no despegable en mis estructuras profundamente anidadas. Que dia. – partofthething

2

Si utiliza dill , su ejemplo no falla en salmuera ...

>>> import dill 
>>> import new 
>>> 
>>> class Test(object): 
...  pass 
... 
>>> def test_func(self): 
...  pass 
... 
>>> test = Test() 
>>> dill.dumps(test) 
'\x80\x02cdill.dill\n_create_type\nq\x00(cdill.dill\n_load_type\nq\x01U\x08TypeTypeq\x02\x85q\x03Rq\x04U\x04Testq\x05h\x01U\nObjectTypeq\x06\x85q\x07Rq\x08\x85q\t}q\n(U\r__slotnames__q\x0b]q\x0cU\n__module__q\rU\x08__main__q\x0eU\x07__doc__q\x0fNutq\x10Rq\x11)\x81q\x12}q\x13b.' 
>>> test.test_meth = new.instancemethod(test_func, test) 
>>> dill.dumps(test) 
'\x80\x02cdill.dill\n_create_type\nq\x00(cdill.dill\n_load_type\nq\x01U\x08TypeTypeq\x02\x85q\x03Rq\x04U\x04Testq\x05h\x01U\nObjectTypeq\x06\x85q\x07Rq\x08\x85q\t}q\n(U\r__slotnames__q\x0b]q\x0cU\n__module__q\rU\x08__main__q\x0eU\x07__doc__q\x0fNutq\x10Rq\x11)\x81q\x12}q\x13U\ttest_methq\x14h\x01U\nMethodTypeq\x15\x85q\x16Rq\x17cdill.dill\n_create_function\nq\x18(cdill.dill\n_unmarshal\nq\x19Ubc\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00C\x00\x00\x00s\x04\x00\x00\x00d\x00\x00S(\x01\x00\x00\x00N(\x00\x00\x00\x00(\x01\x00\x00\x00t\x04\x00\x00\x00self(\x00\x00\x00\x00(\x00\x00\x00\x00s\x07\x00\x00\x00<stdin>t\t\x00\x00\x00test_func\x01\x00\x00\x00s\x02\x00\x00\x00\x00\x01q\x1a\x85q\x1bRq\x1cc__builtin__\n__main__\nU\ttest_funcq\x1dNN}q\x1etq\x1fRq h\x12N\x87q!Rq"sb.' 

Así que tenemos que encontrar algo t sombrero dill no se puede encurtir ...

>>> class Unpicklable(object): 
... def breakme(self): 
...  self.x = iter(set()) 
... 
>>> u = Unpicklable() 
>>> dill.dumps(u) 
'\x80\x02cdill.dill\n_create_type\nq\x00(cdill.dill\n_load_type\nq\x01U\x08TypeTypeq\x02\x85q\x03Rq\x04U\x0bUnpicklableq\x05h\x01U\nObjectTypeq\x06\x85q\x07Rq\x08\x85q\t}q\n(U\r__slotnames__q\x0b]q\x0cU\n__module__q\rU\x08__main__q\x0eU\x07breakmeq\x0fcdill.dill\n_create_function\nq\x10(cdill.dill\n_unmarshal\nq\x11U\xafc\x01\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00C\x00\x00\x00s"\x00\x00\x00d\x01\x00d\x00\x00l\x00\x00}\x01\x00t\x01\x00t\x02\x00\x83\x00\x00\x83\x01\x00|\x00\x00_\x03\x00d\x00\x00S(\x02\x00\x00\x00Ni\xff\xff\xff\xff(\x04\x00\x00\x00t\t\x00\x00\x00itertoolst\x04\x00\x00\x00itert\x03\x00\x00\x00sett\x01\x00\x00\x00x(\x02\x00\x00\x00t\x04\x00\x00\x00selfR\x00\x00\x00\x00(\x00\x00\x00\x00(\x00\x00\x00\x00s\x07\x00\x00\x00<stdin>t\x07\x00\x00\x00breakme\x02\x00\x00\x00s\x04\x00\x00\x00\x00\x01\x0c\x01q\x12\x85q\x13Rq\x14c__builtin__\n__main__\nh\x0fNN}q\x15tq\x16Rq\x17U\x07__doc__q\x18Nutq\x19Rq\x1a)\x81q\x1b}q\x1cb.' 
>>> u.breakme() 
>>> dill.dumps(u) 
Traceback (most recent call last): 
…(snip)… 
pickle.PicklingError: Can't pickle <type 'setiterator'>: it's not found as __builtin__.setiterator 
>>> 

Si el mensaje de error no era bueno, podría usar dill.detect para ver qué objetos unpicklable lo que el objeto de nivel superior contiene.

>>> dill.detect.badobjects(u, depth=1) 
{'__hash__': <method-wrapper '__hash__' of Unpicklable object at 0x10a37b350>, '__setattr__': <method-wrapper '__setattr__' of Unpicklable object at 0x10a37b350>, '__reduce_ex__': <built-in method __reduce_ex__ of Unpicklable object at 0x10a37b350>, '__reduce__': <built-in method __reduce__ of Unpicklable object at 0x10a37b350>, '__str__': <method-wrapper '__str__' of Unpicklable object at 0x10a37b350>, '__format__': <built-in method __format__ of Unpicklable object at 0x10a37b350>, '__getattribute__': <method-wrapper '__getattribute__' of Unpicklable object at 0x10a37b350>, '__delattr__': <method-wrapper '__delattr__' of Unpicklable object at 0x10a37b350>, 'breakme': <bound method Unpicklable.breakme of <__main__.Unpicklable object at 0x10a37b350>>, '__repr__': <method-wrapper '__repr__' of Unpicklable object at 0x10a37b350>, '__dict__': {'x': <setiterator object at 0x10a370820>}, 'x': <setiterator object at 0x10a370820>, '__sizeof__': <built-in method __sizeof__ of Unpicklable object at 0x10a37b350>, '__init__': <method-wrapper '__init__' of Unpicklable object at 0x10a37b350>} 
>>> dill.detect.badtypes(u, depth=1) 
{'__hash__': <type 'method-wrapper'>, '__setattr__': <type 'method-wrapper'>, '__reduce_ex__': <type 'builtin_function_or_method'>, '__reduce__': <type 'builtin_function_or_method'>, '__str__': <type 'method-wrapper'>, '__format__': <type 'builtin_function_or_method'>, '__getattribute__': <type 'method-wrapper'>, '__delattr__': <type 'method-wrapper'>, 'breakme': <type 'instancemethod'>, '__repr__': <type 'method-wrapper'>, '__dict__': <type 'dict'>, 'x': <type 'setiterator'>, '__sizeof__': <type 'builtin_function_or_method'>, '__init__': <type 'method-wrapper'>} 
>>> set(dill.detect.badtypes(u, depth=1).values()) 
set([<type 'dict'>, <type 'method-wrapper'>, <type 'instancemethod'>, <type 'setiterator'>, <type 'builtin_function_or_method'>]) 

dill no se basa en el método __getstate__ estar presente, aunque tal vez debería utilizarlo si es que existe. También puede usar objgraph para obtener una imagen de todas las dependencias de objetos que se utilizan para construir lo que no se acumula. Puede ayudarlo a determinar cuál es la raíz del problema, de acuerdo con la información anterior.

Ver dill.detect uso en la búsqueda de artículos unpicklable en este número: https://github.com/uqfoundation/dill/issues/58

Cuestiones relacionadas