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)