[Zope3-checkins] CVS: Zope3/src/zope/app/fssync - committer.py:1.22 fspickle.py:1.3

Fred L. Drake, Jr. fred at zope.com
Wed Jan 14 16:51:01 EST 2004


Update of /cvs-repository/Zope3/src/zope/app/fssync
In directory cvs.zope.org:/tmp/cvs-serv27321

Modified Files:
	committer.py fspickle.py 
Log Message:
Changes in the Extra and Annotations directories of a zsync checkout
could not be committed because create_object() would use the temporary
container provided by a serialization adapter as a location to perform
adapter looks and other location-sensitive operations; a separate
context needs to be used to provide location information when
traversing objects in the Extra and Annotations directories since
those ephemeral containers provide no locations in the Zope site.

The context which should be used is the innermost object 'o' from
which the root is reachable via __parent__ references.  This is the
only way these objects can perform adapter looks or resolve persistent
references to objects in the tree.  They will (still) not be able to
resolve "parent" references because the extra and annotation values
for object 'o' have no parent.


=== Zope3/src/zope/app/fssync/committer.py 1.21 => 1.22 ===
--- Zope3/src/zope/app/fssync/committer.py:1.21	Tue Jan 13 17:28:46 2004
+++ Zope3/src/zope/app/fssync/committer.py	Wed Jan 14 16:50:30 2004
@@ -214,7 +214,21 @@
             metadata = Metadata()
         self.metadata = metadata
 
-    def synch(self, container, name, fspath):
+    # The Extra and Annotations directories of a zsync need to be
+    # treated specially to allow create_object() to use the the
+    # innermost object 'o' from which the root is reachable via
+    # __parent__ references to perform location-sensitive operations.
+    # This is the only way these objects can perform adapter looks or
+    # resolve persistent references to objects in the tree.  They will
+    # (still) not be able to resolve "parent" references because the
+    # extra and annotation values for object 'o' have no parent.
+    #
+    # This passes around a context object once traversal has entered
+    # the extra and annotation areas of the data area.  This object is
+    # used to provide location if not None; it is only checked by
+    # create_object().
+
+    def synch(self, container, name, fspath, context=None):
         """Synchronize an object or object tree from the filesystem.
 
         SynchronizationError is raised for errors that can't be
@@ -234,14 +248,14 @@
             raise SynchronizationError("invalid separator in name %r" % name)
 
         if not name:
-            self.synch_dir(container, fspath)
+            self.synch_dir(container, fspath, context)
         else:
             try:
                 traverseName(container, name)
             except:
-                self.synch_new(container, name, fspath)
+                self.synch_new(container, name, fspath, context)
             else:
-                self.synch_old(container, name, fspath)
+                self.synch_old(container, name, fspath, context)
 
             # Now update extra and annotations
             try:
@@ -253,13 +267,13 @@
                 extra = adapter.extra()
                 extrapath = fsutil.getextra(fspath)
                 if extra is not None and os.path.exists(extrapath):
-                    self.synch_dir(extra, extrapath)
+                    self.synch_dir(extra, extrapath, obj)
                 ann = self.getAnnotations(obj)
                 annpath = fsutil.getannotations(fspath)
                 if ann is not None and os.path.exists(annpath):
-                    self.synch_dir(ann, annpath)
+                    self.synch_dir(ann, annpath, obj)
 
-    def synch_dir(self, container, fspath):
+    def synch_dir(self, container, fspath, context=None):
         """Helper to synchronize a directory."""
         adapter = self.getSerializer(container)
         nameset = {} # name --> absolute path
@@ -283,22 +297,23 @@
             if os.path.isdir(path):
                 subdirs.append((name, path))
             else:
-                self.synch(container, name, path)
+                self.synch(container, name, path, context)
         # Now do the directories
         for name, path in subdirs:
-            self.synch(container, name, path)
+            self.synch(container, name, path, context)
 
-    def synch_new(self, container, name, fspath):
+    def synch_new(self, container, name, fspath, context=None):
         """Helper to synchronize a new object."""
         entry = self.metadata.getentry(fspath)
         if entry:
-            self.create_object(container, name, entry, fspath)
+            self.create_object(container, name, entry, fspath,
+                               context=context)
             obj = traverseName(container, name)
             adapter = self.getSerializer(obj)
             if IObjectDirectory.isImplementedBy(adapter):
-                self.synch_dir(obj, fspath)
+                self.synch_dir(obj, fspath, context)
 
-    def synch_old(self, container, name, fspath):
+    def synch_old(self, container, name, fspath, context=None):
         """Helper to synchronize an existing object."""
         entry = self.metadata.getentry(fspath)
         if entry.get("flag") == "removed":
@@ -310,11 +325,11 @@
         obj = traverseName(container, name)
         adapter = self.getSerializer(obj)
         if IObjectDirectory.isImplementedBy(adapter):
-            self.synch_dir(obj, fspath)
+            self.synch_dir(obj, fspath, context)
         else:
             if adapter.typeIdentifier() != entry.get("type"):
                 self.create_object(container, name, entry, fspath,
-                                   replace=True)
+                                   replace=True, context=context)
             else:
                 original_fn = fsutil.getoriginal(fspath)
                 if os.path.exists(original_fn):
@@ -332,7 +347,7 @@
                     if not entry.get("factory"):
                         # If there's no factory, we can't call setBody()
                         self.create_object(container, name, entry, fspath,
-                                           True)
+                                           True, context=context)
                         obj = traverseName(container, name)
                     else:
                         adapter.setBody(newdata)
@@ -346,7 +361,8 @@
                     else:
                         publish(obj, ObjectModifiedEvent(obj))
 
-    def create_object(self, container, name, entry, fspath, replace=False):
+    def create_object(self, container, name, entry, fspath, replace=False,
+                      context=None):
         """Helper to create an item in a container or mapping."""
         factory_name = entry.get("factory")
         if factory_name:
@@ -359,8 +375,14 @@
                 data = read_file(fspath)
                 adapter.setBody(data)
         else:
+            if context is None:
+                location = container
+                parent = container
+            else:
+                location = context
+                parent = None
             # No factory; try using IFileFactory or IDirectoryFactory
-            as = getService(container, "Adapters")
+            as = getService(location, "Adapters")
             isuffix = name.rfind(".")
             if isuffix >= 0:
                 suffix = name[isuffix:]
@@ -372,9 +394,9 @@
             else:
                 iface = IFileFactory
 
-            factory = as.queryNamedAdapter(container, iface, suffix)
+            factory = as.queryNamedAdapter(location, iface, suffix)
             if factory is None:
-                factory = as.queryAdapter(container, iface)
+                factory = as.queryAdapter(location, iface)
 
             if iface is IDirectoryFactory:
                 if factory:
@@ -394,7 +416,7 @@
                     # 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)
+                    obj = fspickle.loads(s, location, parent)
 
         set_item(container, name, obj, replace)
 


=== Zope3/src/zope/app/fssync/fspickle.py 1.2 => 1.3 ===
--- Zope3/src/zope/app/fssync/fspickle.py:1.2	Sun Sep 21 13:32:11 2003
+++ Zope3/src/zope/app/fssync/fspickle.py	Wed Jan 14 16:50:30 2004
@@ -34,7 +34,7 @@
 >>> o1.foo = o2
 
 >>> s = dumps(o1)
->>> c1 = loads(s, o1.__parent__)
+>>> c1 = loads(s, o1.__parent__, o1.__parent__)
 >>> c1 is not o1
 1
 >>> c1.data == o1.data
@@ -93,11 +93,12 @@
     data = sio.getvalue()
     return data
 
-def loads(data, parent=None):
-    if parent is None:
-        return cPickle.loads(data)
+def loads(data, location, parent=None):
     sio = StringIO(data)
-    persistent = ParentPersistentLoader(parent)
+    if parent is None:
+        persistent = PersistentLoader(location)
+    else:
+        persistent = ParentPersistentLoader(location, parent)
     u = cPickle.Unpickler(sio)
     u.persistent_load = persistent.load
     return u.load()
@@ -151,32 +152,12 @@
             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
-
-    """
+class PersistentLoader:
 
-    def __init__(self, parent):
-        self.parent = parent
-        self.root = location.LocationPhysicallyLocatable(parent).getRoot()
+    def __init__(self, context):
+        locatable = location.LocationPhysicallyLocatable(context)
+        __traceback_info__ = (context, locatable),
+        self.root = locatable.getRoot()
         self.traverse = zapi.getAdapter(self.root, ITraverser).traverse
 
     def load(self, path):
@@ -186,9 +167,20 @@
                 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 ParentPersistentLoader(PersistentLoader):
+
+    def __init__(self, context, parent):
+        self.parent = parent
+        PersistentLoader.__init__(self, context)
+
+    def load(self, path):
+        if path == PARENT_MARKER:
+            return self.parent
+        else:
+            return PersistentLoader.load(self, path)
 
 
 class DataLocation(location.TLocation):




More information about the Zope3-Checkins mailing list