[Zope3-checkins] CVS: Zope3/src/zope/app/fssync -
fspickle.py:1.1.2.1 classes.py:1.14.2.1 committer.py:1.18.2.3
Fred L. Drake, Jr.
fred at zope.com
Fri Sep 12 12:31:25 EDT 2003
Update of /cvs-repository/Zope3/src/zope/app/fssync
In directory cvs.zope.org:/tmp/cvs-serv18315
Modified Files:
Tag: parentgeddon-branch
classes.py committer.py
Added Files:
Tag: parentgeddon-branch
fspickle.py
Log Message:
when serializing objects using XML pickles, handle parent references as
relative to the object being serialized, and other references to external
objects as absolute path references
=== Added File Zope3/src/zope/app/fssync/fspickle.py ===
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Pickle support functions for fssync.
The functions here generate pickles that understand their location in
the object tree without causing the entire tree to be stored in the
pickle. Persistent objects stored inside the outermost object are
stored entirely in the pickle, and objects stored outside by outermost
object but referenced from within are stored as persistent references.
The parent of the outermost object is treated specially so that the
pickle can be 'unpacked' with a new parent to create a copy in the new
location; unpacking a pickle containing a parent reference requires
passing an object to use as the parent as the second argument to the
loads() function. The name of the outermost object is not stored in
the pickle unless it is stored in the object.
>>> root = location.TLocation()
>>> zope.interface.directlyProvides(root, IContainmentRoot)
>>> o1 = DataLocation('o1', root, 12)
>>> o2 = DataLocation('o2', root, 24)
>>> o3 = DataLocation('o3', o1, 36)
>>> o4 = DataLocation('o4', o3, 48)
>>> o1.foo = o2
>>> s = dumps(o1)
>>> c1 = loads(s, o1.__parent__)
>>> c1 is not o1
1
>>> c1.data == o1.data
1
>>> c1.__parent__ is o1.__parent__
1
>>> c1.foo is o2
1
>>> c3 = c1.o3
>>> c3 is o3
0
>>> c3.__parent__ is c1
1
>>> c3.data == o3.data
1
>>> c4 = c3.o4
>>> c4 is o4
0
>>> c4.data == o4.data
1
>>> c4.__parent__ is c3
1
$Id: fspickle.py,v 1.1.2.1 2003/09/12 16:30:54 fdrake Exp $
"""
import cPickle
from cStringIO import StringIO
import zope.interface
from zope.app import location
from zope.app import zapi
from zope.app.interfaces.location import ILocation
from zope.app.interfaces.traversing import IContainmentRoot
from zope.app.interfaces.traversing import ITraverser
PARENT_MARKER = ".."
# We're not ready to use protocol 2 yet; this can be changed when
# zope.xmlpickle.ppml gets updated to support protocol 2.
PICKLE_PROTOCOL = 1
def dumps(ob):
parent = getattr(ob, '__parent__', None)
if parent is None:
return cPickle.dumps(ob)
sio = StringIO()
persistent = ParentPersistentIdGenerator(ob)
p = cPickle.Pickler(sio, PICKLE_PROTOCOL)
p.persistent_id = persistent.id
p.dump(ob)
data = sio.getvalue()
return data
def loads(data, parent=None):
if parent is None:
return cPickle.loads(data)
sio = StringIO(data)
persistent = ParentPersistentLoader(parent)
u = cPickle.Unpickler(sio)
u.persistent_load = persistent.load
return u.load()
class ParentPersistentIdGenerator:
"""
>>> from zope.app.location import TLocation
>>> root = TLocation()
>>> zope.interface.directlyProvides(root, IContainmentRoot)
>>> o1 = TLocation(); o1.__parent__ = root; o1.__name__ = 'o1'
>>> o2 = TLocation(); o2.__parent__ = root; o2.__name__ = 'o2'
>>> o3 = TLocation(); o3.__parent__ = o1; o3.__name__ = 'o3'
>>> root.o1 = o1
>>> root.o2 = o2
>>> o1.foo = o2
>>> o1.o3 = o3
>>> gen = ParentPersistentIdGenerator(o1)
>>> gen.id(root)
'..'
>>> gen.id(o2)
u'/o2'
>>> gen.id(o3)
>>> gen.id(o1)
>>> gen = ParentPersistentIdGenerator(o3)
>>> gen.id(root)
u'/'
"""
def __init__(self, top):
self.location = top
self.parent = getattr(top, "__parent__", None)
self.root = location.LocationPhysicallyLocatable(top).getRoot()
def id(self, object):
if ILocation.isImplementedBy(object):
if location.inside(object, self.location):
return None
elif object is self.parent:
# XXX emit special parent marker
return PARENT_MARKER
elif location.inside(object, self.root):
return location.LocationPhysicallyLocatable(object).getPath()
raise ValueError(
"object implementing ILocation found outside tree")
else:
return None
class ParentPersistentLoader:
"""
>>> from zope.app.location import TLocation
>>> root = TLocation()
>>> zope.interface.directlyProvides(root, IContainmentRoot)
>>> o1 = TLocation(); o1.__parent__ = root; o1.__name__ = 'o1'
>>> o2 = TLocation(); o2.__parent__ = root; o2.__name__ = 'o2'
>>> o3 = TLocation(); o3.__parent__ = o1; o3.__name__ = 'o3'
>>> root.o1 = o1
>>> root.o2 = o2
>>> o1.foo = o2
>>> o1.o3 = o3
>>> loader = ParentPersistentLoader(o1)
>>> loader.load(PARENT_MARKER) is o1
1
>>> loader.load('/') is root
1
>>> loader.load('/o2') is o2
1
"""
def __init__(self, parent):
self.parent = parent
self.root = location.LocationPhysicallyLocatable(parent).getRoot()
self.traverse = zapi.getAdapter(self.root, ITraverser).traverse
def load(self, path):
if path[:1] == u"/":
# outside object:
if path == "/":
return self.root
else:
return self.traverse(path[1:])
elif path == PARENT_MARKER:
return self.parent
raise ValueError("unknown persistent object reference: %r" % path)
class DataLocation(location.TLocation):
"""Sample data container class used in doctests."""
def __init__(self, name, parent, data):
self.__name__ = name
self.__parent__ = parent
if parent is not None:
setattr(parent, name, self)
self.data = data
super(DataLocation, self).__init__()
=== Zope3/src/zope/app/fssync/classes.py 1.14 => 1.14.2.1 ===
--- Zope3/src/zope/app/fssync/classes.py:1.14 Tue Sep 2 16:32:09 2003
+++ Zope3/src/zope/app/fssync/classes.py Fri Sep 12 12:30:54 2003
@@ -16,10 +16,11 @@
$Id$
"""
+from zope.app.fssync import fspickle
from zope.app.interfaces.fssync import IObjectFile
from zope.app.interfaces.annotation import IAnnotations
from zope.component import queryAdapter
-from zope.xmlpickle import dumps
+from zope.xmlpickle import toxml
from zope.proxy import removeAllProxies
from zope.interface import implements
@@ -95,7 +96,8 @@
def getBody(self):
"See IObjectFile"
- return dumps(self.context)
+ s = fspickle.dumps(self.context)
+ return toxml(s)
def setBody(self, body):
"See IObjectFile"
=== Zope3/src/zope/app/fssync/committer.py 1.18.2.2 => 1.18.2.3 ===
--- Zope3/src/zope/app/fssync/committer.py:1.18.2.2 Mon Sep 8 18:36:53 2003
+++ Zope3/src/zope/app/fssync/committer.py Fri Sep 12 12:30:54 2003
@@ -18,18 +18,17 @@
import os
-from zope.app import zapi
from zope.component import getAdapter, getService
-from zope.xmlpickle import loads
from zope.configuration.name import resolve
-from zope.proxy import removeAllProxies
-
-from zope.fssync.metadata import Metadata
from zope.fssync import fsutil
+from zope.fssync.metadata import Metadata
+from zope.proxy import removeAllProxies
+from zope.xmlpickle import fromxml
-from zope.app.interfaces.fssync import IObjectDirectory, IObjectFile
-
+from zope.app import zapi
+from zope.app.fssync import fspickle
from zope.app.interfaces.container import IContainer, IRemoveSource, IAddTarget
+from zope.app.interfaces.fssync import IObjectDirectory, IObjectFile
from zope.app.traversing import traverseName, getName
from zope.app.interfaces.file import IFileFactory, IDirectoryFactory
from zope.app.event import publish
@@ -383,8 +382,10 @@
obj = factory(name, None, data)
obj = removeAllProxies(obj)
else:
- # Oh well, assume the file is an xml pickle
- obj = load_file(fspath)
+ # The file must contain an xml pickle, or we can't load it:
+ s = read_file(fspath)
+ s = fromxml(s)
+ obj = fspickle.loads(s, container)
set_item(container, name, obj, replace)
@@ -415,14 +416,9 @@
else:
del container[name]
-def load_file(fspath):
- """Helper to load an xml pickle from a file."""
- return loads(read_file(fspath, "r"))
-
-def read_file(fspath, mode="rb"):
+def read_file(fspath):
"""Helper to read the data from a file."""
- assert mode in ("r", "rb")
- f = open(fspath, mode)
+ f = open(fspath, "rb")
try:
data = f.read()
finally:
More information about the Zope3-Checkins
mailing list