durusmail: durus-users: Pickling/exporting objects
Pickling/exporting objects
2005-03-02
2005-03-02
2005-03-02
2005-03-02
2005-03-02
2005-03-02
2005-03-02
2005-03-03
2005-03-03
2005-03-03
2005-03-03
2005-03-03
2005-03-04
2005-03-04
2005-03-04
2005-03-04
2005-03-04
2005-03-04
2005-03-05
Pickling/exporting objects
A.M. Kuchling
2005-03-02
For my application, I need to be able to export a set of objects from
a Durus database.  So far I've been converting objects into an
S-expression-based shorthand form, writing it out, and creating new
objects after reading the S-expressions.  This is high-maintenance,
though: add a new attribute, and you also have to update the S-expr
output code and the input code.

pickle doesn't seem to work on objects that subclass Persistent:
should I expect it to work?  Can I make it work?

As an experiment I wrote the copying function below; it tries to find
the closure of all objects referenced by a starting object.  These
objects then have their _p_oid and _p_connection zapped, and are
stored in a new connection.  It seems to work on a little example.

 * Is the approach taken pure evil, or only slightly naughty?
 * Is the code's usage of Durus functions correct in all particulars?
 * Should this function be added to Durus?

--amk


# Ignore this stuff... this is application-specific.
import os
from ACE_model import get_model, get_connection
m = get_model('small')
print m

# Here's the actual function
from durus import serialize, connection, file_storage

def copy_obj (obj):
    """Copies the object 'obj' into a new FileStorage, including
    all required objects.
    """

    conn = obj._p_connection
    ow = serialize.ObjectWriter(conn)
    root_oid = obj._p_oid
    queue = [root_oid]
    oid_coll = {}
    while len(queue):
        oid = queue.pop(0)
        if oid in oid_coll:
            continue

        # Get object for this OID
        obj = conn.get(oid)
        if obj is None:
            ##print 'no object with oid', repr(oid)
            continue

        oid_coll[oid] = obj

        # Ensure object is loaded
        if obj._p_is_ghost():
            conn.load_state(obj)

        # Get OIDs referenced by this object and add them to the queue
        data, refs = ow.get_state(obj)
        refs = serialize.split_oids(refs)
        queue.extend(refs)

    # Need to put this in a try/finally w/ preceding block
    ow.close()

    print len(oid_coll), 'objects'

    # Copy those objects to new storage
    try:
        os.unlink('/tmp/new-fs')
    except os.error:
        pass
    fs = file_storage.FileStorage('/tmp/new-fs')
    conn2 = connection.Connection(fs)
    root = conn2.get_root()

    # Clear _p_ attributes
    # (should probably clear the original connection's cache after doing this)
    for obj in oid_coll.values():
        obj._p_oid = obj._p_connection = None

    # Store object in new storage
    root['copy'] = oid_coll[root_oid]

    # Commit it and close the FileStorage
    conn2.commit()
    del conn2
    fs.close()

# Make initial copy
s = copy_obj(m)

# Rename file storage
os.rename('/tmp/new-fs', '/tmp/original-fs')
fs = file_storage.FileStorage('/tmp/original-fs')

# Get copy, and try copying it again
conn2 = connection.Connection(fs)
root = conn2.get_root()
m2 = root['copy']
print m2
m2.dump()
copy_obj(m2)


reply