2011-10-21 12 views
5

Estoy usando YAML y SQLAlchemy. Definí mi objeto, y puedo usar YAML para imprimir eso muy bien. Sin embargo, cuando intento utilizar YAML en el objeto devuelto por una consulta SQLAlchemy, falla con el error can't pickle int objects. Imprimí la instancia devuelta desde SQLAlchemy, y muestra el tipo correcto. Voy a dejar que el código hable por:No se puede extraer el error de objeto int cuando el objeto proviene de 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

¿Cuál es el tipo de "instancia"? Un yaml.dump (10) funciona bien, entonces podría ser un tipo de SQLAlchemy que no tiene el método de decapado requerido (es decir,un método __reduce__ que devuelve tipos seleccionables). –

Respuesta

2

Trate de añadir lo siguiente a su clase:

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

Estoy confundido ya que la impresión (yaml.dump (fhash)) funciona bien, por lo que no se queja. ¿Por qué cambiaría el tipo cuando proviene de una consulta? – esac

+0

Después de "imprimir instancia", coloque "tipo de impresión (instancia)". ¿Qué obtienes? –

+0

imprime '' – esac

1

Resulta que el valor por defecto reduce_ex método (im bastante seguro de que este es el uno en object(), pero no tiene que serlo.) que viene de la línea cuando tiene sqlalchemy activo, agrega un miembro _sa_instance_state al 'estado' devuelto en la API reduce_ex que PyYAML usa para realizar la serialización.

Al serializar un objeto procedente de una consulta SqlAlchemy, esta es esencialmente una parte oculta de los metadatos del objeto, a la que se puede acceder para realizar más operaciones.

Este es el objeto en el que el serializador PyYAML está fallando. Puede verificar esto ejecutando su serialización en PDB y viendo dos llamadas para representar_objeto en su pila de llamadas, incluso para resultados de objeto de consulta SQLAlchemy relativamente simples.

Este enlace de instancia de consulta se utiliza, según tengo entendido, para dar poder a los métodos con lo que permite recuperar la consulta que genera un objeto dado dentro de la vida del mismo intérprete de Python.

Si te importa esa funcionalidad (cosas como session.new & session.dirty), necesitarás implementar soporte para eso en el serializador PyYAML.

Si no le importa, y sólo desea que los miembros declarados, se puede utilizar una clase base que 'esconde' que la vinculación de las llamadas a reducir * - en cuenta que esto también va a romper la extensión serializador como SQLAlchemy bien, entonces, considere sus planes cuidadosamente.

Un ejemplo de una clase base para implementar ese cambio es:

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 

Esto entonces le permitirá ida y vuelta los objetos de entrada/salida de yaml, aunque un ida y vuelta será disociar desde cualquier transacción pendiente o consultas. Esto también podría tener interacciones si está usando miembros con carga lenta, por ejemplo. Asegúrese de estar serializando todo lo que espera.

NOTA/EDIT: que optó por utilizar reduce_ex aquí, para ser compatibles con posibles otras clases de base o mixins. De acuerdo con https://docs.python.org/2/library/pickle.html#object.reduce_ex, esto producirá el comportamiento correcto para cualquier clase base, y también detectará si se ha declarado ().

Redux ... reducimos devolverá el real dict del objeto de instancia - que no queremos eliminar desde allí, por lo que para __reduce *, se debe copiar en realidad poco profundo que dict.

Cuestiones relacionadas