[Checkins] SVN: zope.copy/trunk/ Initial import.

Dan Korostelev nadako at gmail.com
Sun Feb 8 11:15:43 EST 2009


Log message for revision 96250:
  Initial import.

Changed:
  _U  zope.copy/trunk/
  A   zope.copy/trunk/CHANGES.txt
  A   zope.copy/trunk/LICENSE.txt
  A   zope.copy/trunk/bootstrap.py
  A   zope.copy/trunk/buildout.cfg
  A   zope.copy/trunk/setup.py
  A   zope.copy/trunk/src/
  A   zope.copy/trunk/src/zope/
  A   zope.copy/trunk/src/zope/__init__.py
  A   zope.copy/trunk/src/zope/copy/
  A   zope.copy/trunk/src/zope/copy/README.txt
  A   zope.copy/trunk/src/zope/copy/__init__.py
  A   zope.copy/trunk/src/zope/copy/interfaces.py
  A   zope.copy/trunk/src/zope/copy/tests.py

-=-

Property changes on: zope.copy/trunk
___________________________________________________________________
Added: svn:ignore
   + bin
parts
.installed.cfg
.project
.pydevproject


Added: zope.copy/trunk/CHANGES.txt
===================================================================
--- zope.copy/trunk/CHANGES.txt	                        (rev 0)
+++ zope.copy/trunk/CHANGES.txt	2009-02-08 16:15:43 UTC (rev 96250)
@@ -0,0 +1,9 @@
+=======
+CHANGES
+=======
+
+3.5.0 (unreleased)
+------------------
+
+- Initial release. The functionality was extracted from ``zc.copy`` to
+  provide a generic object copying mechanism with minimal dependencies.


Property changes on: zope.copy/trunk/CHANGES.txt
___________________________________________________________________
Added: svn:keywords
   + Id

Added: zope.copy/trunk/LICENSE.txt
===================================================================
--- zope.copy/trunk/LICENSE.txt	                        (rev 0)
+++ zope.copy/trunk/LICENSE.txt	2009-02-08 16:15:43 UTC (rev 96250)
@@ -0,0 +1,54 @@
+Zope Public License (ZPL) Version 2.1
+-------------------------------------
+
+A copyright notice accompanies this license document that
+identifies the copyright holders.
+
+This license has been certified as open source. It has also
+been designated as GPL compatible by the Free Software
+Foundation (FSF).
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the
+following conditions are met:
+
+1. Redistributions in source code must retain the
+   accompanying copyright notice, this list of conditions,
+   and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the accompanying
+   copyright notice, this list of conditions, and the
+   following disclaimer in the documentation and/or other
+   materials provided with the distribution.
+
+3. Names of the copyright holders must not be used to
+   endorse or promote products derived from this software
+   without prior written permission from the copyright
+   holders.
+
+4. The right to distribute this software or to use it for
+   any purpose does not give you the right to use
+   Servicemarks (sm) or Trademarks (tm) of the copyright
+   holders. Use of them is covered by separate agreement
+   with the copyright holders.
+
+5. If any files are modified, you must cause the modified
+   files to carry prominent notices stating that you changed
+   the files and the date of any change.
+
+Disclaimer
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS``
+  AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+  NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+  AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
+  NO EVENT SHALL THE COPYRIGHT HOLDERS BE
+  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+  DAMAGE.


Property changes on: zope.copy/trunk/LICENSE.txt
___________________________________________________________________
Added: svn:keywords
   + Id

Added: zope.copy/trunk/bootstrap.py
===================================================================
--- zope.copy/trunk/bootstrap.py	                        (rev 0)
+++ zope.copy/trunk/bootstrap.py	2009-02-08 16:15:43 UTC (rev 96250)
@@ -0,0 +1,52 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (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.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+
+$Id$
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+ez = {}
+exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+                     ).read() in ez
+ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+import pkg_resources
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+if sys.platform == 'win32':
+    cmd = '"%s"' % cmd # work around spawn lamosity on windows
+
+ws = pkg_resources.working_set
+assert os.spawnle(
+    os.P_WAIT, sys.executable, sys.executable,
+    '-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout',
+    dict(os.environ,
+         PYTHONPATH=
+         ws.find(pkg_resources.Requirement.parse('setuptools')).location
+         ),
+    ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout')
+import zc.buildout.buildout
+zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
+shutil.rmtree(tmpeggs)


Property changes on: zope.copy/trunk/bootstrap.py
___________________________________________________________________
Added: svn:keywords
   + Id

Added: zope.copy/trunk/buildout.cfg
===================================================================
--- zope.copy/trunk/buildout.cfg	                        (rev 0)
+++ zope.copy/trunk/buildout.cfg	2009-02-08 16:15:43 UTC (rev 96250)
@@ -0,0 +1,7 @@
+[buildout]
+develop = .
+parts = test
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = zope.copy [test]


Property changes on: zope.copy/trunk/buildout.cfg
___________________________________________________________________
Added: svn:keywords
   + Id

Added: zope.copy/trunk/setup.py
===================================================================
--- zope.copy/trunk/setup.py	                        (rev 0)
+++ zope.copy/trunk/setup.py	2009-02-08 16:15:43 UTC (rev 96250)
@@ -0,0 +1,57 @@
+##############################################################################
+#
+# Copyright (c) 2009 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (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.
+#
+##############################################################################
+"""Setup for zope.copy package
+
+$Id$
+"""
+import os
+from setuptools import setup, find_packages
+
+def read(*rnames):
+    return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
+
+setup(name = 'zope.copy',
+      version = '3.5.0dev',
+      author='Zope Foundation and Contributors',
+      author_email='zope-dev at zope.org',
+      description='Pluggable object copying mechanism',
+      long_description=(
+          read('src', 'zope', 'copy', 'README.txt')
+          + '\n\n' +
+          read('CHANGES.txt')
+          ),
+      keywords = "zope3 copying cloning",
+      classifiers = [
+          'Development Status :: 5 - Production/Stable',
+          'Framework :: Zope3',
+          'Intended Audience :: Developers',
+          'License :: OSI Approved :: Zope Public License',
+          'Operating System :: OS Independent',
+          'Programming Language :: Python',
+          'Topic :: Database',
+          ],
+      url='http://pypi.python.org/pypi/zope.copy',
+      license='ZPL 2.1',
+      packages=find_packages('src'),
+      package_dir = {'': 'src'},
+      namespace_packages=['zope'],
+      extras_require={'test': ['zope.component',
+                               'zope.location',
+                               'zope.testing']},
+      install_requires = ['setuptools',
+                          'zope.interface',
+                          ],
+      include_package_data = True,
+      zip_safe = False,
+      )


Property changes on: zope.copy/trunk/setup.py
___________________________________________________________________
Added: svn:keywords
   + Id


Property changes on: zope.copy/trunk/src
___________________________________________________________________
Added: svn:ignore
   + zope.copy.egg-info


Added: zope.copy/trunk/src/zope/__init__.py
===================================================================
--- zope.copy/trunk/src/zope/__init__.py	                        (rev 0)
+++ zope.copy/trunk/src/zope/__init__.py	2009-02-08 16:15:43 UTC (rev 96250)
@@ -0,0 +1,7 @@
+# this is a namespace package
+try:
+    import pkg_resources
+    pkg_resources.declare_namespace(__name__)
+except ImportError:
+    import pkgutil
+    __path__ = pkgutil.extend_path(__path__, __name__)


Property changes on: zope.copy/trunk/src/zope/__init__.py
___________________________________________________________________
Added: svn:keywords
   + Id

Added: zope.copy/trunk/src/zope/copy/README.txt
===================================================================
--- zope.copy/trunk/src/zope/copy/README.txt	                        (rev 0)
+++ zope.copy/trunk/src/zope/copy/README.txt	2009-02-08 16:15:43 UTC (rev 96250)
@@ -0,0 +1,321 @@
+==============
+Object copying
+==============
+
+This package provides a pluggable way to copy persistent objects. It
+was once extracted from the zc.copy package to contain much less
+dependencies. In fact, we only depend on zope.interface to provide
+pluggability.
+
+The package provides a ``clone`` function that does the object cloning
+and the ``copy`` wrapper that sets __parent__ and __name__ attributes
+of object's copy to None. This is useful, when working with Zope's
+located objects (see zope.location package). The ``copy`` function
+actually calls the ``clone`` function so we'll use the first one in
+the examples below. We'll also look a bit at their differences in the
+end of this document. 
+
+The ``clone`` function (and thus the ``copy`` function that wraps it)
+uses pickling to copy the object and all its subobjects recursively.
+As each object and subobject is pickled, the function tries to adapt it
+to ``zope.copy.interfaces.ICopyHook``. If a copy hook is found,
+the recursive copy is halted.  The hook is called with two values: the
+main, top-level object that is being copied; and a callable that supports
+registering functions to be called after the copy is made. The copy hook
+should return the exact object or subobject that should be used at this
+point in the copy, or raise ``zope.copy.interfaces.ResumeCopy``
+exception to resume copying the object or subobject recursively after
+all.
+
+Note that we use zope's component architecture provided by the
+``zope.component`` package in this document, but the
+``zope.copy`` package itself doesn't use or depend on it, so
+you can provide another adaptation mechanism as described in zope.interface's
+adapter documentation.
+
+Simple hooks
+------------
+
+First let's examine a simple use. A hook is to support the use case of
+resetting the state of data that should be changed in a copy -- for
+instance, a log, or freezing or versioning data. The canonical way to
+do this is by storing the changable data on a special sub-object of the
+object that is to be copied. We'll look at a simple case of a subobject
+that should be converted to None when it is copied -- the way that the
+zc.freeze copier hook works. Also see the zc.objectlog copier module
+for a similar example.
+
+So, here is a simple object that stores a boolean on a special object.
+
+    >>> class Demo(object):
+    ...     _frozen = None
+    ...     def isFrozen(self):
+    ...         return self._frozen is not None
+    ...     def freeze(self):
+    ...         self._frozen = Data()
+    ...
+    >>> class Data(object):
+    ...     pass
+    ...
+
+Here's what happens if we copy one of these objects without a copy hook.
+
+    >>> original = Demo()
+    >>> original.isFrozen()
+    False
+    >>> original.freeze()
+    >>> original.isFrozen()
+    True
+    >>> import zope.copy
+    >>> copy = zope.copy.copy(original)
+    >>> copy is original
+    False
+    >>> copy.isFrozen()
+    True
+
+Now let's make a super-simple copy hook that always returns None, no
+matter what the top-level object being copied is.  We'll register it and
+make another copy.
+
+    >>> import zope.component
+    >>> import zope.interface
+    >>> import zope.copy.interfaces
+    >>> def _factory(obj, register):
+    ...     return None
+    >>> @zope.component.adapter(Data)
+    ... @zope.interface.implementer(zope.copy.interfaces.ICopyHook)
+    ... def data_copyfactory(obj):
+    ...     return _factory
+    ...
+
+    >>> zope.component.provideAdapter(data_copyfactory)
+    >>> copy2 = zope.copy.copy(original)
+    >>> copy2 is original
+    False
+    >>> copy2.isFrozen()
+    False
+
+Much better.
+
+Post-copy functions
+-------------------
+
+Now, let's look at the registration function that the hook can use.  It
+is useful for resetting objects within the new copy -- for instance, back
+references such as __parent__ pointers.  This is used concretely in the
+zc.objectlog.copier module; we will come up with a similar but artificial
+example here.
+
+Imagine an object with a subobject that is "located" (i.e., zope.location) on
+the parent and should be replaced whenever the main object is copied.
+
+    >>> import zope.location.location
+    >>> class Subobject(zope.location.location.Location):
+    ...     def __init__(self):
+    ...         self.counter = 0
+    ...     def __call__(self):
+    ...         res = self.counter
+    ...         self.counter += 1
+    ...         return res
+    ...
+    >>> o = zope.location.location.Location()
+    >>> s = Subobject()
+    >>> o.subobject = s
+    >>> zope.location.location.locate(s, o, 'subobject')
+    >>> s.__parent__ is o
+    True
+    >>> o.subobject()
+    0
+    >>> o.subobject()
+    1
+    >>> o.subobject()
+    2
+
+Without an ICopyHook, this will simply duplicate the subobject, with correct
+new pointers.
+
+    >>> c = zope.copy.copy(o)
+    >>> c.subobject.__parent__ is c
+    True
+
+Note that the subobject has also copied state.
+
+    >>> c.subobject()
+    3
+    >>> o.subobject()
+    3
+
+Our goal will be to make the counters restart when they are copied.  We'll do
+that with a copy hook.
+
+This copy hook is different: it provides an object to replace the old object,
+but then it needs to set it up further after the copy is made.  This is
+accomplished by registering a callable, ``reparent`` here, that sets up the
+__parent__.  The callable is passed a function that can translate something
+from the original object into the equivalent on the new object.  We use this
+to find the new parent, so we can set it.
+
+    >>> import zope.component
+    >>> import zope.interface
+    >>> import zope.copy.interfaces
+    >>> @zope.component.adapter(Subobject)
+    ... @zope.interface.implementer(zope.copy.interfaces.ICopyHook)
+    ... def subobject_copyfactory(original):
+    ...     def factory(obj, register):
+    ...         obj = Subobject()
+    ...         def reparent(translate):
+    ...             obj.__parent__ = translate(original.__parent__)
+    ...         register(reparent)
+    ...         return obj
+    ...     return factory
+    ...
+    >>> zope.component.provideAdapter(subobject_copyfactory)
+
+Now when we copy, the new subobject will have the correct, revised __parent__,
+but will be otherwise reset (here, just the counter)
+
+    >>> c = zope.copy.copy(o)
+    >>> c.subobject.__parent__ is c
+    True
+    >>> c.subobject()
+    0
+    >>> o.subobject()
+    4
+
+Resuming recursive copy
+-----------------------
+
+One thing we didn't examine yet is the use of ResumeCopy exception in
+the copy hooks. For example, when copying located objects we don't want
+to copy referenced subobjects that are not located in the object that
+is being copied. Imagine, we have a content object that has an image object,
+referenced by the ``cover`` attribute, but located in an independent
+place.
+
+    >>> root = zope.location.location.Location()
+
+    >>> content = zope.location.location.Location()
+    >>> zope.location.location.locate(content, root, 'content')
+
+    >>> image = zope.location.location.Location()
+    >>> zope.location.location.locate(image, root, 'image.jpg')
+
+    >>> content.cover = image
+  
+Without any hooks, the image object will be cloned as well:
+
+    >>> new = zope.copy.copy(content)
+    >>> new.cover is image
+    False
+
+That's not what we'd expect though, so, let's provide a copy hook
+to deal with that. The copy hook for this case is provided by zope.location
+package, but we'll create one from scratch as we want to check out the
+usage of the ResumeCopy. 
+
+    >>> @zope.component.adapter(zope.location.interfaces.ILocation)
+    ... @zope.interface.implementer(zope.copy.interfaces.ICopyHook)
+    ... def location_copyfactory(obj):
+    ...     def factory(location, register):
+    ...         if not zope.location.location.inside(obj, location):
+    ...             return obj
+    ...         raise zope.copy.interfaces.ResumeCopy
+    ...     return factory
+    ...
+    >>> zope.component.provideAdapter(location_copyfactory)
+
+This hook returns objects as they are if they are not located inside
+object that's being copied, or raises ResumeCopy to signal that the
+recursive copy should be continued and used for the object.
+
+    >>> new = zope.copy.copy(content)
+    >>> new.cover is image
+    True
+
+Much better :-)
+
+``clone`` vs ``copy``
+---------------------
+
+As we stated before, there's two functions that is used for copying
+objects. The ``clone`` - that does the job, and its wrapper, ``copy``
+that calls ``clone`` and then clears copy's __parent__ and __name__
+attribute values.
+
+Let's create a location object with __name__ and __parent__ set.
+
+    >>> root = zope.location.location.Location()
+    >>> folder = zope.location.location.Location()
+    >>> folder.__name__ = 'files'
+    >>> folder.__parent__ = root
+
+The ``clone`` function will leave those attributes as is. Note that the
+referenced __parent__ won't be cloned, as we registered a hook for locations
+in the previous section.
+
+    >>> folder_clone = zope.copy.clone(folder)
+    >>> folder_clone.__parent__ is root
+    True
+    >>> folder_clone.__name__ == 'files'
+    True
+
+However, the ``copy`` function will reset those attributes to None, as
+we will probably want to place our object into another container with
+another name.
+
+    >>> folder_clone = zope.copy.copy(folder)
+    >>> folder_clone.__parent__ is None
+    True
+    >>> folder_clone.__name__ is None
+    True
+
+Notice, that if your object doesn't have __parent__ and __name__
+attributes at all, or these attributes could'nt be got or set because of
+some protections (as with zope.security's proxies, for example), you still
+can use the ``copy`` function, because it works for objects that don't
+have those attributes.
+
+It won't set them if original object doesn't have them:
+
+    >>> class Something(object):
+    ...     pass
+
+    >>> s = Something()
+    >>> s_copy = zope.copy.copy(s)
+    >>> s_copy.__parent__
+    Traceback (most recent call last):
+    ...
+    AttributeError: ...
+    >>> s_copy.__name__
+    Traceback (most recent call last):
+    ...
+    AttributeError: ...
+
+And it won't fail if original object has them but doesn't allow to set
+them.
+
+    >>> root = object()
+    >>> class Something(object):
+    ...
+    ...     @apply
+    ...     def __name__():
+    ...         def fget(self):
+    ...             return 'something'
+    ...         def fset(self, value):
+    ...             raise AttributeError
+    ...         return property(fget, fset)
+    ...
+    ...     @apply
+    ...     def __parent__():
+    ...         def fget(self):
+    ...             return root
+    ...         def fset(self, value):
+    ...             raise AttributeError
+    ...         return property(fget, fset)
+
+    >>> s = Something()
+    >>> s_copy = zope.copy.copy(s)
+    >>> s_copy.__parent__ is root
+    True
+    >>> s_copy.__name__ == 'something'
+    True


Property changes on: zope.copy/trunk/src/zope/copy/README.txt
___________________________________________________________________
Added: svn:keywords
   + Id

Added: zope.copy/trunk/src/zope/copy/__init__.py
===================================================================
--- zope.copy/trunk/src/zope/copy/__init__.py	                        (rev 0)
+++ zope.copy/trunk/src/zope/copy/__init__.py	2009-02-08 16:15:43 UTC (rev 96250)
@@ -0,0 +1,97 @@
+##############################################################################
+#
+# Copyright (c) 2009 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (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.
+#
+##############################################################################
+"""
+$Id$
+"""
+import tempfile
+import cPickle
+
+from zope.copy import interfaces
+
+def clone(obj):
+    """Clone an object by pickling and unpickling it"""
+    tmp = tempfile.TemporaryFile()
+    persistent = CopyPersistent(obj)
+
+    # Pickle the object to a temporary file
+    pickler = cPickle.Pickler(tmp, 2)
+    pickler.persistent_id = persistent.id
+    pickler.dump(obj)
+
+    # Now load it back
+    tmp.seek(0)
+    unpickler = cPickle.Unpickler(tmp)
+    unpickler.persistent_load = persistent.load
+
+    res = unpickler.load()
+    # run the registered cleanups
+    def convert(obj):
+        return unpickler.memo[pickler.memo[id(obj)][0]]
+    for call in persistent.registered:
+        call(convert)
+    return res
+
+def copy(obj):
+    """Clone an object, clearing the __name__ and __parent__ attribute
+    values of the copy."""
+    res = clone(obj)
+    if getattr(res, '__parent__', None) is not None:
+        try:
+            res.__parent__ = None
+        except AttributeError:
+            pass
+    if getattr(res, '__name__', None) is not None:
+        try:
+            res.__name__ = None
+        except AttributeError:
+            pass
+    return res
+
+class CopyPersistent(object):
+    """A helper class providing the persisntent_id and persistent_load
+    functions for pickling and unpickling respectively.
+    
+    It uses the adaptation to ICopyHook to allow control over object
+    copying. See README.txt for more information on that mechanism.
+    """
+
+    def __init__(self, obj):
+        self.toplevel = obj
+        self.pids_by_id = {}
+        self.others_by_pid = {}
+        self.load = self.others_by_pid.get
+        self.registered = []
+
+    def id(self, obj):
+        hook = interfaces.ICopyHook(obj, None)
+        if hook is not None:
+            oid = id(obj)
+            if oid in self.pids_by_id:
+                return self.pids_by_id[oid]
+            try:
+                res = hook(self.toplevel, self.registered.append)
+            except interfaces.ResumeCopy:
+                pass
+            else:
+                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[oid] = pid
+                self.others_by_pid[pid] = res
+                return pid
+        return None


Property changes on: zope.copy/trunk/src/zope/copy/__init__.py
___________________________________________________________________
Added: svn:keywords
   + Id

Added: zope.copy/trunk/src/zope/copy/interfaces.py
===================================================================
--- zope.copy/trunk/src/zope/copy/interfaces.py	                        (rev 0)
+++ zope.copy/trunk/src/zope/copy/interfaces.py	2009-02-08 16:15:43 UTC (rev 96250)
@@ -0,0 +1,44 @@
+##############################################################################
+#
+# Copyright (c) 2009 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (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.
+#
+##############################################################################
+"""
+$Id$
+"""
+import zope.interface
+
+class ResumeCopy(Exception):
+    """Don't use the hook, resume the copy.
+    
+    This is a special exception, raised from the copy hook to signal
+    copier that it should continue copying the object recursively.
+    
+    See ICopyHook.__call__ method documentation.
+    """
+
+class ICopyHook(zope.interface.Interface):
+    """An adapter to an object that is being copied"""
+    
+    def __call__(obj, register):
+        """Given the top-level object that is being copied, return the
+        version of the adapted object that should be used in the new copy.
+
+        Raising ResumeCopy means that you are foregoing the hook: the
+        adapted object will continue to be recursively copied as usual.
+
+        If you need to have a post-copy actions executed, register a
+        callable with ``register``.  This callable must take a single
+        argument: a callable that, given an object from the original,
+        returns the equivalent in the copy.
+        
+        See README.txt for more explanation.
+        """


Property changes on: zope.copy/trunk/src/zope/copy/interfaces.py
___________________________________________________________________
Added: svn:keywords
   + Id

Added: zope.copy/trunk/src/zope/copy/tests.py
===================================================================
--- zope.copy/trunk/src/zope/copy/tests.py	                        (rev 0)
+++ zope.copy/trunk/src/zope/copy/tests.py	2009-02-08 16:15:43 UTC (rev 96250)
@@ -0,0 +1,32 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (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.
+#
+##############################################################################
+"""Tests for the zope.copy package.
+$Id$
+"""
+import unittest
+from zope.testing import doctest, module
+
+def setUp(test):
+    module.setUp(test, 'zope.copy.doctest')
+
+def tearDown(test):
+    module.tearDown(test)
+
+def test_suite():
+    return unittest.TestSuite((
+        doctest.DocFileSuite('README.txt',
+            setUp=setUp,
+            tearDown=tearDown,
+            optionflags=doctest.ELLIPSIS|doctest.NORMALIZE_WHITESPACE),
+        ))


Property changes on: zope.copy/trunk/src/zope/copy/tests.py
___________________________________________________________________
Added: svn:keywords
   + Id



More information about the Checkins mailing list