[Checkins] SVN: zope.location/trunk/ Provide ICopyHook adapter for zope.copy, deprecate locationCopy and CopyPersistent helper.

Dan Korostelev nadako at gmail.com
Sun Feb 8 18:08:16 EST 2009


Log message for revision 96291:
  Provide ICopyHook adapter for zope.copy, deprecate locationCopy and CopyPersistent helper.

Changed:
  U   zope.location/trunk/CHANGES.txt
  U   zope.location/trunk/setup.py
  U   zope.location/trunk/src/zope/location/configure.zcml
  U   zope.location/trunk/src/zope/location/pickling.py

-=-
Modified: zope.location/trunk/CHANGES.txt
===================================================================
--- zope.location/trunk/CHANGES.txt	2009-02-08 23:03:50 UTC (rev 96290)
+++ zope.location/trunk/CHANGES.txt	2009-02-08 23:08:16 UTC (rev 96291)
@@ -5,7 +5,17 @@
 3.5.3 (unreleased)
 ------------------
 
-- ...
+- Use new zope.copy package for implementing location copying. Thus
+  there's changes in the ``zope.locaton.pickling`` module:
+  
+   * The ``locationCopy`` and ``CopyPersistent`` was removed in prefer
+     to their equivalents in zope.copy. Deprecated backward-compatibility
+     imports provided.
+   
+   * The module now provides a ``zope.copy.interfaces.ICopyHook`` adapter
+     for ``ILocation`` objects that replaces the old CopyPersistent
+     functionality of checking for the need to clone objects based on
+     their location.
 
 3.5.2 (2009-02-04)
 ------------------

Modified: zope.location/trunk/setup.py
===================================================================
--- zope.location/trunk/setup.py	2009-02-08 23:03:50 UTC (rev 96290)
+++ zope.location/trunk/setup.py	2009-02-08 23:08:16 UTC (rev 96291)
@@ -56,6 +56,7 @@
                         'zope.schema>=3.5.1dev',
                         'zope.component',
                         'zope.proxy>3.3',
+                        'zope.copy',
                         ],
       include_package_data = True,
       zip_safe = False,

Modified: zope.location/trunk/src/zope/location/configure.zcml
===================================================================
--- zope.location/trunk/src/zope/location/configure.zcml	2009-02-08 23:03:50 UTC (rev 96290)
+++ zope.location/trunk/src/zope/location/configure.zcml	2009-02-08 23:08:16 UTC (rev 96291)
@@ -1,7 +1,8 @@
 <configure xmlns="http://namespaces.zope.org/zope">
 
+  <adapter factory=".location.LocationProxy" />
+  <adapter factory=".pickling.LocationCopyHook" />
   <adapter factory=".traversing.LocationPhysicallyLocatable" />
   <adapter factory=".traversing.RootPhysicallyLocatable" />
-  <adapter factory=".location.LocationProxy" />
 
 </configure>

Modified: zope.location/trunk/src/zope/location/pickling.py
===================================================================
--- zope.location/trunk/src/zope/location/pickling.py	2009-02-08 23:03:50 UTC (rev 96290)
+++ zope.location/trunk/src/zope/location/pickling.py	2009-02-08 23:08:16 UTC (rev 96291)
@@ -17,147 +17,75 @@
 """
 __docformat__ = 'restructuredtext'
 
-import cPickle
-import tempfile
-import zope.interface
-import zope.location.interfaces
+from zope.component import adapts
+from zope.copy.interfaces import ICopyHook, ResumeCopy
+from zope.deferredimport import deprecated
+from zope.interface import implements
 
-from zope.location.interfaces import ILocation
-from zope.location.location import Location, inside
+import zope.location.interfaces
+from zope.location.location import inside
 from zope.location.traversing import LocationPhysicallyLocatable
 
-def locationCopy(loc):
-    r"""Return a copy of an object, and anything in it
+class LocationCopyHook(object):
+    """Copy hook to preserve copying referenced objects that are not
+    located inside object that's being copied.
 
-    If object in the location refer to objects outside of the
-    location, then the copies of the objects in the location refer to
-    the same outside objects.
-
-    For example, suppose we have an object (location) hierarchy like this::
-
-           o1
-          /  \
-        o2    o3
-        |     |
-        o4    o5
-
-    >>> o1 = Location()
-    >>> o1.o2 = Location(); o1.o2.__parent__ = o1
-    >>> o1.o3 = Location(); o1.o3.__parent__ = o1
-    >>> o1.o2.o4 = Location(); o1.o2.o4.__parent__ = o1.o2
-    >>> o1.o3.o5 = Location(); o1.o3.o5.__parent__ = o1.o3
-
-    In addition, o3 has a non-location reference to o4.
-
-    >>> o1.o3.o4 = o1.o2.o4
-
-    When we copy o3, we should get a copy of o3 and o5, with
-    references to o1 and o4.
-
-    >>> c3 = locationCopy(o1.o3)
-    >>> c3 is o1.o3
+    To see the problem, imagine we want to copy an ILocation object that
+    contains an attribute-based reference to another ILocation object
+    and the referenced object is not contained inside object being copied. 
+    
+    Without this hook, the referenced object will be cloned:
+    
+    >>> from zope.location.location import Location, locate
+    >>> root = Location()
+    >>> page = Location()
+    >>> locate(page, root, 'page')
+    >>> image = Location()
+    >>> locate(page, root, 'image')
+    >>> page.thumbnail = image
+    
+    >>> from zope.copy import copy
+    >>> page_copy = copy(page)
+    >>> page_copy.thumbnail is image
     False
-    >>> c3.__parent__ is o1
-    True
-    >>> c3.o5 is o1.o3.o5
-    False
-    >>> c3.o5.__parent__ is c3
-    True
-    >>> c3.o4 is o1.o2.o4
-    True
 
-    """
-    tmp = tempfile.TemporaryFile()
-    persistent = CopyPersistent(loc)
+    But if we will provide a hook, the attribute will point to the
+    original object as we might want.
 
-    # Pickle the object to a temporary file
-    pickler = cPickle.Pickler(tmp, 2)
-    pickler.persistent_id = persistent.id
-    pickler.dump(loc)
+    >>> from zope.component import provideAdapter
+    >>> provideAdapter(LocationCopyHook)
 
-    # Now load it back
-    tmp.seek(0)
-    unpickler = cPickle.Unpickler(tmp)
-    unpickler.persistent_load = persistent.load
-
-    return unpickler.load()
-
-
-class CopyPersistent(object):
-    """Persistence hooks for copying locations
-
-    See `locationCopy` above.
-
-    We get initialized with an initial location:
-
-    >>> o1 = Location()
-    >>> persistent = CopyPersistent(o1)
-
-    We provide an `id` function that returns None when given a non-location:
-
-    >>> persistent.id(42)
-
-    Or when given a location that is inside the initial location:
-
-    >>> persistent.id(o1)
-    >>> o2 = Location(); o2.__parent__ = o1
-    >>> persistent.id(o2)
-
-    But, if we get a location outside the original location, we assign
-    it an `id` and return the `id`:
-
-    >>> o3 = Location()
-    >>> id3 = persistent.id(o3)
-    >>> id3 is None
-    False
-    >>> o4 = Location()
-    >>> id4 = persistent.id(o4)
-    >>> id4 is None
-    False
-    >>> id4 is id3
-    False
-
-    If we ask for the `id` of an outside location more than once, we
-    always get the same `id` back:
-
-    >> persistent.id(o4) == id4
+    >>> from zope.copy import copy
+    >>> page_copy = copy(page)
+    >>> page_copy.thumbnail is image
     True
-
-    We also provide a load function that returns the objects for which
-    we were given ids:
-
-    >>> persistent.load(id3) is o3
-    True
-    >>> persistent.load(id4) is o4
-    True
-
+    
     """
+    
+    adapts(zope.location.interfaces.ILocation)
+    implements(ICopyHook)
+    
+    def __init__(self, context):
+        self.context = context
+    
+    def __call__(self, toplevel, register):
+        if not inside(self.context, toplevel):
+            return self.context
+        raise ResumeCopy
+        
+# BBB 2009/02/09
+deprecated(
+    'The locationCopy was replaced by more generic "clone" function'
+    'in the zope.copy package. This reference may be removed someday.',
+    locationCopy='zope.copy:clone'
+    )
+deprecated(
+    'The CopyPersistent was made more generic and moved to the'
+    'zope.copy package. This reference may be removed someday.',
+    CopyPersistent='zope.copy:CopyPersistent',
+)
 
-    def __init__(self, location):
-        self.location = location
-        self.pids_by_id = {}
-        self.others_by_pid = {}
-        self.load = self.others_by_pid.get
-
-    def id(self, object):
-        if ILocation.providedBy(object):
-            if not inside(object, self.location):
-                if id(object) in self.pids_by_id:
-                    return self.pids_by_id[id(object)]
-                pid = len(self.others_by_pid)
-
-                # The following is needed to overcome a bug
-                # in pickle.py. The pickle checks the boolean value
-                # of the id, rather than whether it is None.
-                pid += 1
-
-                self.pids_by_id[id(object)] = pid
-                self.others_by_pid[pid] = object
-                return pid
-
-        return None
-
-
+# XXX: is this actually used anywhere? (nadako, 2009/02/09)
 class PathPersistent(object):
     """Persistence hooks for pickling locations
 
@@ -176,6 +104,7 @@
 
     We get initialized with an initial location:
 
+    >>> from zope.location.location import Location
     >>> o1 = Location()
     >>> persistent = PathPersistent(o1)
 
@@ -193,9 +122,9 @@
     path. To compute it's path, it must be rooted:
 
     >>> from zope.location.tests import TLocation
+    >>> from zope.interface import directlyProvides
     >>> root = TLocation()
-    >>> zope.interface.directlyProvides(
-    ...     root, zope.location.interfaces.IRoot)
+    >>> directlyProvides(root, zope.location.interfaces.IRoot)
     >>> o3 = TLocation()
     >>> o3.__name__ = 'o3'
     >>> o3.__parent__ = root
@@ -233,7 +162,7 @@
         self.location = location
 
     def id(self, object):
-        if ILocation.providedBy(object):
+        if zope.location.interfaces.ILocation.providedBy(object):
             if not inside(object, self.location):
                 return LocationPhysicallyLocatable(object).getPath()
         return None



More information about the Checkins mailing list