[Zope-CVS] CVS: Products/ZopeVersionControl - Repository.py:1.4 Version.py:1.7 ZopeRepository.py:1.3 ZopeVersion.py:1.3
Shane Hathaway
shane@zope.com
Wed, 15 Jan 2003 17:18:19 -0500
Update of /cvs-repository/Products/ZopeVersionControl
In directory cvs.zope.org:/tmp/cvs-serv29711
Modified Files:
Repository.py Version.py ZopeRepository.py ZopeVersion.py
Log Message:
Containers are now versioned independently of their ObjectManagerish contents.
This means it is now possible to put versioned objects in a hierarchy.
The current implementation ignores the fact that the user may actually want
to version the item names and item history IDs. Instead, it only
versions the aspects of the container that are not items.
=== Products/ZopeVersionControl/Repository.py 1.3 => 1.4 ===
--- Products/ZopeVersionControl/Repository.py:1.3 Wed Jun 12 13:57:43 2002
+++ Products/ZopeVersionControl/Repository.py Wed Jan 15 17:17:46 2003
@@ -63,8 +63,8 @@
"""Internal: replace a persistent object in its container."""
container = aq_parent(aq_inner(object))
item_id = object.getId()
- container._delObject(item_id)
- container._setObject(item_id, new_object)
+ container._delOb(item_id)
+ container._setOb(item_id, new_object)
return container._getOb(item_id)
#####################################################################
=== Products/ZopeVersionControl/Version.py 1.6 => 1.7 ===
--- Products/ZopeVersionControl/Version.py:1.6 Wed Jun 12 13:57:43 2002
+++ Products/ZopeVersionControl/Version.py Wed Jan 15 17:17:46 2003
@@ -13,12 +13,49 @@
__version__='$Revision$'[11:-2]
-from Acquisition import Implicit, aq_parent, aq_inner
+import tempfile
+import time
+from cStringIO import StringIO
+from cPickle import Pickler, Unpickler
+
+from Acquisition import Implicit, aq_parent, aq_inner, aq_base
from Globals import InitializeClass, Persistent
from AccessControl import ClassSecurityInfo
-from Utility import VersionControlError
from BTrees.OOBTree import OOBTree
-import tempfile, time
+
+from Utility import VersionControlError
+
+
+def cloneByPickle(object, ignore_list=()):
+ """Makes a copy of a ZODB object, loading ghosts as needed.
+
+ Ignores specified objects along the way, replacing them with None
+ in the copy.
+ """
+ ignore_dict = {}
+ for o in ignore_list:
+ ignore_dict[id(o)] = o
+
+ def persistent_id(ob, ignore_dict=ignore_dict):
+ if ignore_dict.has_key(id(ob)):
+ return 'ignored'
+ if getattr(ob, '_p_changed', 0) is None:
+ ob._p_changed = 0
+ return None
+
+ def persistent_load(ref):
+ assert ref == 'ignored'
+ return None
+
+ stream = StringIO()
+ p = Pickler(stream, 1)
+ p.persistent_id = persistent_id
+ p.dump(object)
+ stream.seek(0)
+ u = Unpickler(stream)
+ u.persistent_load = persistent_load
+ return u.load()
+
class Version(Implicit, Persistent):
"""A Version is a resource that contains a copy of a particular state
@@ -61,19 +98,7 @@
def stateCopy(self, object, container):
"""Get a deep copy of the state of an object, breaking any database
identity references."""
- # TODO: this should probably use Utility._findModificationTime,
- # though we should gauge the impact on performance.
- if (getattr(object, '_p_changed', 0) or
- getattr(object, '_p_jar', None) is None or
- getattr(container, '_p_jar', None) is None):
- get_transaction().commit(1)
- file = tempfile.TemporaryFile()
- object._p_jar.exportFile(object._p_oid, file)
- file.seek(0)
- result = container._p_jar.importFile(file)
- file.close()
- return result
-
+ return cloneByPickle(aq_base(object))
InitializeClass(Version)
=== Products/ZopeVersionControl/ZopeRepository.py 1.2 => 1.3 ===
--- Products/ZopeVersionControl/ZopeRepository.py:1.2 Thu May 9 13:43:40 2002
+++ Products/ZopeVersionControl/ZopeRepository.py Wed Jan 15 17:17:46 2003
@@ -17,6 +17,8 @@
from SequenceWrapper import SequenceWrapper
import OFS, AccessControl, Acquisition
import Repository
+from Acquisition import aq_base
+from OFS.ObjectManager import ObjectManager
class ZopeRepository(
@@ -83,6 +85,55 @@
security.declarePrivate('objectItems')
def objectItems(self, spec=None):
return SequenceWrapper(self, self._histories.items(), 1)
+
+ security.declarePrivate('listItems')
+ def listContainedItems(self, object):
+ """Internal: list the items of a container as [(id, value)]."""
+ items = []
+ try:
+ v = isinstance(object, ObjectManager)
+ except TypeError:
+ # Python 2.1 isinstance() dislikes ExtensionClass instances.
+ # This is an adequate workaround.
+ v = 0
+ if v:
+ # Return the items.
+ for name, value in object.objectItems():
+ items.append((name, aq_base(value)))
+ return items
+
+ security.declarePrivate('addItems')
+ def restoreContainedItems(self, ob, items):
+ """Internal: restore the items of a container."""
+ # First build "keep", a dictionary listing which
+ # items the object were provided by the version controlled object.
+ # Don't restore over those.
+ keep = {}
+ for name in ob.objectIds():
+ keep[name] = 1
+ # Restore the items.
+ for name, value in items:
+ if not keep.has_key(name):
+ ob._setOb(name, value)
+ if not hasattr(ob, '_tree'):
+ # Avoid generating events, since nothing was ever really
+ # removed or added.
+ ob._objects += ({'meta_type': value.meta_type,
+ 'id': name},)
+ # If there is a _tree attribute, it's very likely
+ # a BTreeFolder2, which doesn't need or want the
+ # _objects attribute.
+ # XXX This is a hackish way to check for BTreeFolder2s.
+
+ security.declarePrivate('replaceObject')
+ def replaceObject(self, object, new_object):
+ """Internal: replace a persistent object in its container."""
+ items = self.listContainedItems(object)
+ ob = Repository.Repository.replaceObject(self, object, new_object)
+ if items:
+ self.restoreContainedItems(ob, items)
+ return ob
+
InitializeClass(ZopeRepository)
=== Products/ZopeVersionControl/ZopeVersion.py 1.2 => 1.3 ===
--- Products/ZopeVersionControl/ZopeVersion.py:1.2 Thu May 9 13:43:40 2002
+++ Products/ZopeVersionControl/ZopeVersion.py Wed Jan 15 17:17:46 2003
@@ -16,6 +16,21 @@
from Globals import DTMLFile, InitializeClass
import OFS, AccessControl, Acquisition
import Version
+from Acquisition import aq_base
+from OFS.ObjectManager import ObjectManager
+
+
+def cloneObjectManager(object):
+ """Makes a copy of an object manager without its subitems.
+ """
+ ids = object.objectIds()
+ values = object.objectValues()
+ object = Version.cloneByPickle(object, values)
+ for id in ids:
+ object._delOb(id)
+ if object._objects:
+ object._objects = ()
+ return object
class ZopeVersion(
@@ -62,6 +77,22 @@
self, REQUEST, manage_tabs_message=message
)
+
+ security.declarePrivate('stateCopy')
+ def stateCopy(self, object, container):
+ """Get a deep copy of the state of an object, breaking any database
+ identity references."""
+ object = aq_base(object)
+ try:
+ v = isinstance(object, ObjectManager)
+ except TypeError:
+ # Python 2.1 isinstance() dislikes ExtensionClass instances.
+ # This is an adequate workaround.
+ v = 0
+ if v:
+ return cloneObjectManager(object)
+ else:
+ return Version.cloneByPickle(object)
InitializeClass(ZopeVersion)