2009-02-20 16 views
30

Quando si pickle un oggetto che ha alcune caratteristiche che non possono essere in salamoia verrà a mancare con un messaggio di errore generico come:Come distinguere quale pickle di attributo dell'oggetto ha esito negativo?

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

Esiste un modo per dire quale attributo ha causato l'eccezione? Sto usando Python 2.5.2.

Anche se capisco in linea di principio la causa principale del problema (ad esempio nell'esempio precedente con un metodo di istanza), può essere ancora molto difficile per individuarlo esattamente. Nel mio caso ho già definito un metodo personalizzato __getstate__, ma ho dimenticato un attributo critico. Questo è successo in una struttura complicata di oggetti nidificati, quindi mi ci è voluto un po 'per identificare l'attributo cattivo.

Come richiesto, ecco un semplice esempio erano salamoia intenzionalmente fallisce:

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) 

Questa è l'uscita:

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 

Purtroppo non c'è traccia che l'attributo test_meth causa il problema.

+0

si può forse dare un piccolo esempio di un attributo fallendo? o almeno mostrare un po 'più del traceback per vedere dove nel modulo pickle fallisce? – MrTopf

+0

oh, e quale versione di Python stai usando? – MrTopf

+0

@MrTopf: aggiunte le informazioni – nikow

risposta

14

È possibile presentare un bug contro Python per non includere più messaggi di errore utili. Nel frattempo, modificare la funzione _reduce_ex() in copy_reg.py.

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

uscita:

<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

perché non inserire semplicemente "self" nel messaggio di errore anziché nella stampa? – MrTopf

+1

Credo che questo copra l'eccezione "TypeError" nell'esempio, tuttavia l'eccezione originale "PicklingError" non è indirizzata. – cmcginty

+0

L'errore TypeError causa PicklingError. Una volta che si interrompe il tentativo di piclettare gli oggetti instancemethod (o qualsiasi altro oggetto non selezionabile), tutto dovrebbe funzionare. – joeforker

8

Ho avuto lo stesso problema come voi, ma le mie classi sono state un po 'più complicato (vale a dire un grande albero di oggetti simili) in modo che la stampa non ha aiutato tanto Ho hackerato una funzione di aiuto. Non è completo ed è previsto solo per l'uso con il protocollo di picking 2: È stato sufficiente per individuare i miei problemi. Se si desidera estenderlo per coprire tutto, il protocollo è descritto in http://www.python.org/dev/peps/pep-0307/ ho reso questo post modificabile in modo che tutti possano aggiornare il codice.

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 esempio di utilizzo in cui K è l'oggetto che non PIckle

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

Ciò significa che l'attibute K._gen non è serializzabili e lo stesso vale per K._base._gens .

+0

Grazie, aiuta. E penso che '__getstate __()' debba essere definito nella classe corrispondente. –

3

ho scoperto che se si sottoclasse Pickler e avvolgere il metodo Pickler.save() in una prova, ad eccezione di blocco

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

Poi lo chiamano in questo modo

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

Non è solo l'avvio di un debugger? C'è qualche differenza, ad es., Usando semplicemente Eclipse PyDev Debugger per esaminare il problema? – nikow

+0

Se il debugger PyDev si avvia sull'eccezione e ti mette nel giusto livello di esecuzione, è lo stesso. Comunque non uso un IDE. –

+0

L'ho usato per individuare un errore di Pickle particolarmente difficile da trovare. Ma piuttosto che avere la clausola except avvia il debugger, l'ho appena stampato obj e sollevato l'eccezione. Quindi, l'esecuzione di MyPickler (output) .dump (obj) ha prodotto un report di tipo traceback esattamente dove l'oggetto non rimpicciolivo si trovava nelle mie strutture profondamente annidate. Che giornata. – partofthething

2

Se si utilizza dill , il tuo esempio non manca di salamoia ...

>>> 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.' 

Quindi dobbiamo trovare qualcosa di t il cappello dill non può decapitare ...

>>> 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 
>>> 

Se il messaggio di errore non è stato buono, potrei usare dill.detect per vedere che cosa gli oggetti unpicklable l'oggetto di livello superiore 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 non si basa sul metodo __getstate__ essendo presente, anche se forse dovrebbe utilizzare se esiste. È inoltre possibile utilizzare objgraph per ottenere un'immagine di tutte le dipendenze oggetto utilizzate per creare la cosa che non viene decapata. Può aiutarti a capire qual è la radice del problema, in base alle informazioni di cui sopra.

Vedi dill.detect uso nel rintracciare elementi unpicklable in questo numero: https://github.com/uqfoundation/dill/issues/58

Problemi correlati