[Zope-CVS] CVS: Products/ZopeVersionControl - container.py:1.1 IVersionControl.py:1.4 Repository.py:1.6 Version.py:1.8 ZopeRepository.py:1.4 ZopeVersion.py:1.6
Shane Hathaway
shane@zope.com
Tue, 13 May 2003 18:12:32 -0400
Update of /cvs-repository/Products/ZopeVersionControl
In directory cvs.zope.org:/tmp/cvs-serv25790
Modified Files:
IVersionControl.py Repository.py Version.py ZopeRepository.py
ZopeVersion.py
Added Files:
container.py
Log Message:
Refined container version control.
- Added a new interface, IVersionedContainer, for separating contained
items from containers during version control operations.
- Added container.py, a module for adapting objects to
IVersionedContainer. An object can implement this interface to
override the default container version control.
- Changed Version.py and Repository.py to use the new features in
container.py.
- Moved ObjectManager versioning to container.py.
- Renamed "object" to "obj" where I came across it. This is for
Python 2.2+ compatibility.
- Removed unused imports.
=== Added File Products/ZopeVersionControl/container.py ===
##############################################################################
#
# Copyright (c) 2001 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
#
##############################################################################
"""Container versioning support.
$Id: container.py,v 1.1 2003/05/13 22:12:31 shane Exp $
"""
from Acquisition import aq_base
from OFS.ObjectManager import ObjectManager
from IVersionControl import IVersionedContainer
def getContainerVCAdapter(obj):
"""Returns an IVersionedContainer for any object.
This is a super-simplistic adapter implementation.
"""
base = aq_base(obj)
# If the object implements IVersionedContainer, let it say
# what its items are.
if IVersionedContainer.isImplementedBy(base):
return obj
# If the object is an ObjectManager, use the ObjectManager adapter.
try:
is_obj_mgr = isinstance(base, ObjectManager)
except TypeError:
# Python 2.1 isinstance() dislikes ExtensionClass instances.
# This is an adequate workaround.
pass
else:
if is_obj_mgr:
return ObjectManagerVCAdapter(obj)
# Otherwise use the do-nothing adapter.
return DefaultVCAdapter(obj)
def getVCItems(obj):
return getContainerVCAdapter(obj).getVCItems()
def removeVCItems(obj):
getContainerVCAdapter(obj).removeVCItems()
def restoreVCItems(obj, dict):
getContainerVCAdapter(obj).restoreVCItems(dict)
class DefaultVCAdapter:
"""Versioned container adapter for things that are not containers."""
__implements__ = IVersionedContainer
def __init__(self, obj):
self.obj = obj
def getVCItems(self):
"""Returns a mapping that maps item ID to (unwrapped) item."""
return ()
def removeVCItems(self):
"""Removes all versionable items of this container."""
pass
def restoreVCItems(self, dict):
"""Restores items to this container."""
pass
class ObjectManagerVCAdapter:
"""Versioned container adapter for object managers."""
__implements__ = IVersionedContainer
def __init__(self, obj):
self.obj = obj
def getVCItems(self):
"""Returns a mapping that maps item ID to (unwrapped) item."""
res = {}
for name, value in self.obj.objectItems():
res[name] = aq_base(value)
return res
def removeVCItems(self):
"""Removes all versionable items from this container."""
obj = self.obj
for name in obj.objectIds():
obj._delOb(name)
if obj._objects:
obj._objects = ()
def restoreVCItems(self, dict):
"""Restores items to this container."""
# First build "ignore", a dictionary listing which
# items of the object were stored in the repository.
# Don't restore over those.
obj = self.obj
ignore = {}
for name in obj.objectIds():
ignore[name] = 1
# Restore the items of the container.
for name, value in dict.items():
if not ignore.has_key(name):
obj._setOb(name, aq_base(value))
if not hasattr(obj, '_tree'):
# Avoid generating events, since nothing was ever really
# removed or added.
obj._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.
=== Products/ZopeVersionControl/IVersionControl.py 1.3 => 1.4 ===
--- Products/ZopeVersionControl/IVersionControl.py:1.3 Tue Feb 11 16:49:23 2003
+++ Products/ZopeVersionControl/IVersionControl.py Tue May 13 18:12:31 2003
@@ -177,7 +177,7 @@
Permission: Use version control
"""
-class IVersionInfo:
+class IVersionInfo(Interface):
"""The IVersionInfo interface provides access to version control
bookkeeping information. The fields provided by this interface
are:
@@ -202,7 +202,7 @@
information was created.
"""
-class ILogEntry:
+class ILogEntry(Interface):
"""The ILogEntry interface provides access to the information in an
audit log entry. The fields provided by this interface are:
@@ -224,4 +224,21 @@
path - the path to the object upon which the action was taken.
"""
+
+
+class IVersionedContainer(Interface):
+ """Container version control modifier.
+
+ Containerish objects implement this interface to allow the items they
+ contain to be versioned independently of the container.
+ """
+
+ def getVCItems():
+ """Returns a mapping that maps item ID to (unwrapped) item."""
+
+ def removeVCItems():
+ """Removes independently versioned items from this container."""
+
+ def restoreVCItems(dict):
+ """Restores versioned items to this container."""
=== Products/ZopeVersionControl/Repository.py 1.5 => 1.6 ===
--- Products/ZopeVersionControl/Repository.py:1.5 Tue Feb 11 16:49:23 2003
+++ Products/ZopeVersionControl/Repository.py Tue May 13 18:12:31 2003
@@ -13,6 +13,8 @@
__version__='$Revision$'[11:-2]
+import time
+
from Acquisition import Implicit, aq_parent, aq_inner
from ZopeVersionHistory import ZopeVersionHistory
from Globals import InitializeClass, Persistent
@@ -22,8 +24,10 @@
from DateTime.DateTime import DateTime
from BTrees.OOBTree import OOBTree
from BTrees.OIBTree import OIBTree
+
from EventLog import LogEntry
-import time, Utility
+import Utility
+from container import getVCItems, restoreVCItems
class Repository(Implicit, Persistent):
@@ -59,13 +63,17 @@
return self._histories[history_id].__of__(self)
security.declarePrivate('replaceObject')
- def replaceObject(self, object, new_object):
+ def replaceObject(self, obj, new_obj):
"""Internal: replace a persistent object in its container."""
- container = aq_parent(aq_inner(object))
- item_id = object.getId()
+ subitems = getVCItems(obj)
+ container = aq_parent(aq_inner(obj))
+ item_id = obj.getId()
container._delOb(item_id)
- container._setOb(item_id, new_object)
- return container._getOb(item_id)
+ container._setOb(item_id, new_obj)
+ res = container._getOb(item_id)
+ if subitems:
+ restoreVCItems(res, subitems)
+ return res
#####################################################################
# This is the implementation of the public version control interface.
=== Products/ZopeVersionControl/Version.py 1.7 => 1.8 ===
--- Products/ZopeVersionControl/Version.py:1.7 Wed Jan 15 17:17:46 2003
+++ Products/ZopeVersionControl/Version.py Tue May 13 18:12:31 2003
@@ -24,9 +24,10 @@
from BTrees.OOBTree import OOBTree
from Utility import VersionControlError
+from container import getVCItems, removeVCItems
-def cloneByPickle(object, ignore_list=()):
+def cloneByPickle(obj, ignore_list=()):
"""Makes a copy of a ZODB object, loading ghosts as needed.
Ignores specified objects along the way, replacing them with None
@@ -50,7 +51,7 @@
stream = StringIO()
p = Pickler(stream, 1)
p.persistent_id = persistent_id
- p.dump(object)
+ p.dump(obj)
stream.seek(0)
u = Unpickler(stream)
u.persistent_load = persistent_load
@@ -63,7 +64,7 @@
version is created by checking in a checked-out resource. The state
of a version of a version-controlled resource never changes."""
- def __init__(self, version_id, object):
+ def __init__(self, version_id, obj):
self.id = version_id
self.date_created = time.time()
self._data = None
@@ -84,10 +85,10 @@
return self.id
security.declarePrivate('saveState')
- def saveState(self, object):
+ def saveState(self, obj):
"""Save the state of object as the state for this version of
a version-controlled resource."""
- self._data = self.stateCopy(object, self)
+ self._data = self.stateCopy(obj, self)
security.declarePrivate('copyState')
def copyState(self):
@@ -95,10 +96,18 @@
return self.stateCopy(self._data, self)
security.declarePrivate('stateCopy')
- def stateCopy(self, object, container):
+ def stateCopy(self, obj, container):
"""Get a deep copy of the state of an object, breaking any database
identity references."""
- return cloneByPickle(aq_base(object))
+ map = getVCItems(obj)
+ ignore = []
+ if map:
+ for k, v in map.items():
+ ignore.append(aq_base(v))
+ res = cloneByPickle(aq_base(obj), ignore)
+ if map:
+ removeVCItems(res)
+ return res
InitializeClass(Version)
=== Products/ZopeVersionControl/ZopeRepository.py 1.3 => 1.4 ===
--- Products/ZopeVersionControl/ZopeRepository.py:1.3 Wed Jan 15 17:17:46 2003
+++ Products/ZopeVersionControl/ZopeRepository.py Tue May 13 18:12:31 2003
@@ -15,10 +15,8 @@
from Globals import DTMLFile, InitializeClass
from SequenceWrapper import SequenceWrapper
-import OFS, AccessControl, Acquisition
+import OFS, AccessControl
import Repository
-from Acquisition import aq_base
-from OFS.ObjectManager import ObjectManager
class ZopeRepository(
@@ -85,56 +83,6 @@
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.5 => 1.6 ===
--- Products/ZopeVersionControl/ZopeVersion.py:1.5 Wed Jan 15 17:34:57 2003
+++ Products/ZopeVersionControl/ZopeVersion.py Tue May 13 18:12:31 2003
@@ -14,23 +14,8 @@
__version__='$Revision$'[11:-2]
from Globals import DTMLFile, InitializeClass
-import OFS, AccessControl, Acquisition
+import OFS, AccessControl
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 = list(object.objectIds())
- values = [aq_base(o) for o in object.objectValues()]
- object = Version.cloneByPickle(object, values)
- for id in ids:
- object._delOb(id)
- if object._objects:
- object._objects = ()
- return object
class ZopeVersion(
@@ -77,22 +62,5 @@
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)