2011-10-21 9 views
5

Sto usando YAML e SQLAlchemy. Ho definito il mio oggetto, e sono in grado di usare YAML per stampare ciò che va bene. Tuttavia, quando provo a utilizzare YAML sull'oggetto restituito da una query SQLAlchemy, non riesce con l'errore can't pickle int objects. Ho stampato l'istanza restituita da SQLAlchemy e mostra il tipo corretto. Ti lascio il codice a parlare:Non è possibile mettere a picco l'errore dell'oggetto int quando l'oggetto proviene da SQLAlchemy?

class HashPointer(Base): 
    __tablename__ = 'hash_pointers' 

    id = Column(Integer, primary_key=True) 
    hash_code = Column(VARBINARY(64), unique=True) 
    file_pointer = Column(Text) 

    def __init__(self, hash_code, file_pointer): 
     self.hash_code = hash_code 
     self.file_pointer = file_pointer 

    def __repr__(self): 
     return "<HashPointer('%s', '%s')>" % (self.hash_code, self.file_pointer) 

from sqlalchemy import create_engine 
from sqlalchemy.orm import sessionmaker 
Engine = create_engine("mysql://user:[email protected]/db", echo=True) 
Session = sessionmaker(bind=Engine) 
session = Session() 
fhash = HashPointer(0x661623708235, "c:\\test\\001.txt") 

# PRINTS FINE 
print(yaml.dump(fhash)) 

for instance in session.query(HashPointer).all(): 
    # PRINTS FINE AS __repr__ 
    print instance 

    # THROWS ERROR, 'CAN'T PICKLE INT OBJECTS' 
    print(yaml.dump(instance)) 
+0

Qual è il tipo di "istanza"? Un yaml.dump (10) funziona bene, quindi potrei essere un tipo SQLAlchemy che non ha il metodo di decapaggio richiesto (ad es.un metodo __reduce__ che restituisce di per sé i tipi selezionabili). –

risposta

2

Prova ad aggiungere il seguente alla classe:

def __reduce__(self): 
    'Return state information for pickling' 
    return self.__class__, (int(self.hash_code), str(self.file_pointer)) 
+1

Sono confuso da quando la stampa (yaml.dump (fshsh)) funziona bene, quindi non si lamenta. Perché il tipo cambia quando proviene da una query? – esac

+0

Dopo "print instance", inserire "print type (instance)". Che cosa ottieni? –

+0

stampa '' – esac

1

Si scopre che il reduce_ex metodo predefinito (im abbastanza sicuro che questo è l'unico in object(), ma non deve essere.) che scende lungo la linea quando hai sqlalchemy attivo, aggiunge un membro _sa_instance_state allo 'stato' restituito nell'API reduc_ex che PyYAML usa per eseguire la serializzazione.

Durante la serializzazione di un oggetto proveniente da una query SqlAlchemy, questa è essenzialmente una parte nascosta dei metadati dell'oggetto, che è accessibile per ulteriori operazioni.

È questo oggetto in cui il serializzatore PyYAML non funziona. È possibile verificare ciò eseguendo la serializzazione in PDB e visualizzando due chiamate a represent_object nello stack di chiamate, anche per risultati di oggetti query SQLAlchemy relativamente semplici.

Questo collegamento di istanza di query viene utilizzato, a quanto ho capito, per alimentare i metodi con la possibilità di tornare alla query che genera un determinato oggetto all'interno della stessa vita dell'interprete python.

Se ti interessa questa funzionalità (cose come session.new & session.dirty), dovrai implementare il supporto per tale nel serializzatore di PyYAML.

Se non vi interessa, e vogliono solo i vostri membri dichiarati, è possibile utilizzare una classe base che 'nasconde' che il collegamento dalle chiamate a ridurre i * - di notare che questo sarà anche rompere l'estensione serializzatore SQLAlchemy come bene però, quindi considera attentamente i tuoi piani.

Un esempio di una classe base per attuare tale cambiamento è:

DeclBase = declarative_base() 

class Base(DeclBase): 
    __abstract__ = True 

    def __reduce_ex__(self, proto): 
     ret = super(Base, self).__reduce_ex__(proto) 
     ret = (ret[0], ret[1], dict(ret[2])) + ret[3:] 
     ret[2].pop('_sa_instance_state', None) # remove bad yamly from reduce state 
     return ret 

Questo sarà quindi permetterà di andata e ritorno gli oggetti in/out di YAML, anche se un viaggio di andata li dissociarsi da qualsiasi transazione in sospeso o interrogazioni. Questo potrebbe anche avere interazioni se stai usando membri carichi pigri, per un esempio. Assicurati di serializzare tutto ciò che ti aspetti.

NOTA/MODIFICA: Ho scelto di utilizzare reduce_ex qui, per essere compatibile con possibili altre classi base o mixaggi. Secondo https://docs.python.org/2/library/pickle.html#object.reduce_ex, questo produrrà il comportamento corretto per qualsiasi classe base, rilevando anche se è stato dichiarato solo ridurre().

Redux ... riduciamo restituirà l'attuale dict dell'oggetto istanza - non vogliamo cancellare in da lì, quindi per __reduce *, dobbiamo copia in realtà poco profondo che dict.