[Zope-CVS] CVS: Products/ZopeVersionControl - nonversioned.py:1.1
CHANGES.txt:1.2 IVersionControl.py:1.6 Repository.py:1.10
Version.py:1.9 container.py:NONE
Shane Hathaway
shane at zope.com
Fri Jan 30 14:00:10 EST 2004
Update of /cvs-repository/Products/ZopeVersionControl
In directory cvs.zope.org:/tmp/cvs-serv9102
Modified Files:
CHANGES.txt IVersionControl.py Repository.py Version.py
Added Files:
nonversioned.py
Removed Files:
container.py
Log Message:
Refined the pattern for keeping parts of objects out of version control.
This is a generalization of the mechanism for versioning container
items. IVersionedContainer is now named INonVersionedData and has
more descriptive method names.
You can also now supply an attribute, __vc_ignore__, that lists the names
of attributes to be ignored when reverting to an earlier revision.
=== Added File Products/ZopeVersionControl/nonversioned.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
#
##############################################################################
"""Support for non-versioned data embedded in versioned objects.
$Id: nonversioned.py,v 1.1 2004/01/30 18:59:39 shane Exp $
"""
from Acquisition import aq_base
from OFS.ObjectManager import ObjectManager
from IVersionControl import INonVersionedData
try:
# Optional support for references.
from Products.References.Proxy import proxyBase
except ImportError:
isProxy = None
else:
def isProxy(obj):
return (proxyBase(obj) is not aq_base(obj))
def getNonVersionedDataAdapter(obj):
"""Returns an INonVersionedData adapter for any object.
This is a super-simplistic adapter implementation.
"""
base = aq_base(obj)
# If the object implements INonVersionedData, let it say
# what its items are.
if INonVersionedData.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 ObjectManagerNonVersionedDataAdapter(obj)
# Otherwise use the standard adapter.
return StandardNonVersionedDataAdapter(obj)
def listNonVersionedObjects(obj):
return getNonVersionedDataAdapter(obj).listNonVersionedObjects()
def getNonVersionedData(obj):
return getNonVersionedDataAdapter(obj).getNonVersionedData()
def removeNonVersionedData(obj):
getNonVersionedDataAdapter(obj).removeNonVersionedData()
def restoreNonVersionedData(obj, dict):
getNonVersionedDataAdapter(obj).restoreNonVersionedData(dict)
class StandardNonVersionedDataAdapter:
"""Non-versioned data adapter for arbitrary things.
"""
__implements__ = INonVersionedData
def __init__(self, obj):
self.obj = obj
# __vc_ignore__, if set, is a tuple of attribute names to
# manage independently of version control.
self.attrs = getattr(obj, "__vc_ignore__", ())
def listNonVersionedObjects(self):
# Assume it's OK to clone all of the attributes.
# They will be removed later by removeNonVersionedData.
return ()
def removeNonVersionedData(self):
for attr in self.attrs:
try:
delattr(aq_base(self.obj), attr)
except (AttributeError, KeyError):
pass
def getNonVersionedData(self):
data = {}
for attr in self.attrs:
if hasattr(self.obj, attr):
data[attr] = aq_base(getattr(aq_base(self.obj), attr))
return data
def restoreNonVersionedData(self, data):
for attr in self.attrs:
if data.has_key(attr):
setattr(aq_base(self.obj), attr, data[attr])
class ObjectManagerNonVersionedDataAdapter(StandardNonVersionedDataAdapter):
"""Non-versioned data adapter for object managers.
"""
__implements__ = INonVersionedData
def listNonVersionedObjects(self):
contents = self.getNonVersionedData()['contents']
return contents.values()
def removeNonVersionedData(self):
StandardNonVersionedDataAdapter.removeNonVersionedData(self)
obj = self.obj
removed = {}
contents = self.getNonVersionedData()['contents']
for name, value in contents.items():
obj._delOb(name)
removed[name] = 1
if obj._objects:
obj._objects = tuple([info for info in obj._objects
if not removed.has_key(info['id'])])
def getNonVersionedData(self):
contents = {}
attributes = StandardNonVersionedDataAdapter.getNonVersionedData(self)
for name, value in self.obj.objectItems():
if value is not None and isProxy is not None:
if not getattr(value, '_versionable', 1):
# Version the state of subobjects that won't be
# versioned independently.
continue
if isProxy(value):
# Version references in their containers.
continue
contents[name] = aq_base(value)
return {'contents': contents, 'attributes': attributes}
def restoreNonVersionedData(self, data):
StandardNonVersionedDataAdapter.restoreNonVersionedData(
self, data['attributes'])
# First build "ignore", a dictionary that lists which
# items 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 data['contents'].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/CHANGES.txt 1.1 => 1.2 ===
--- Products/ZopeVersionControl/CHANGES.txt:1.1 Mon Oct 27 15:19:15 2003
+++ Products/ZopeVersionControl/CHANGES.txt Fri Jan 30 13:59:39 2004
@@ -1,4 +1,9 @@
-Version 0.3
+Version 0.3 (not yet released)
+
+ - Refined the pattern for maintaining parts of objects independently
+ of version control. This is a generalization of the mechanism for
+ versioning container items. IVersionedContainer is now named
+ INonVersionedData and has more descriptive method names.
- updateResource() and uncheckoutResource() now retain the identity
of the object being versioned. That is, they never replace an
=== Products/ZopeVersionControl/IVersionControl.py 1.5 => 1.6 ===
--- Products/ZopeVersionControl/IVersionControl.py:1.5 Mon Jan 19 15:32:59 2004
+++ Products/ZopeVersionControl/IVersionControl.py Fri Jan 30 13:59:39 2004
@@ -229,19 +229,39 @@
"""
-class IVersionedContainer(Interface):
- """Container version control modifier.
+class INonVersionedData(Interface):
+ """Controls what parts of an object fall outside version control.
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 listNonVersionedObjects():
+ """Returns a list of subobjects that should not be pickled.
- def removeVCItems():
- """Removes independently versioned items from this container."""
+ The objects in the list must not be wrapped, because only the
+ identity of the objects will be considered. The version
+ repository uses this method to avoid cloning subobjects that
+ will soon be removed by removeNonVersionedData.
+ """
- def restoreVCItems(dict):
- """Restores versioned items to this container."""
+ def removeNonVersionedData():
+ """Removes the non-versioned data from this object.
+ The version repository uses this method before storing an
+ object in the version repository.
+ """
+
+ def getNonVersionedData():
+ """Returns an opaque object containing the non-versioned data.
+
+ The version repository uses this method before reverting an
+ object to a revision.
+ """
+
+ def restoreNonVersionedData(dict):
+ """Restores non-versioned data to this object.
+
+ The version repository uses this method after reverting an
+ object to a revision.
+ """
=== Products/ZopeVersionControl/Repository.py 1.9 => 1.10 ===
--- Products/ZopeVersionControl/Repository.py:1.9 Mon Jan 19 15:32:59 2004
+++ Products/ZopeVersionControl/Repository.py Fri Jan 30 13:59:39 2004
@@ -27,7 +27,7 @@
from EventLog import LogEntry
import Utility
-from container import getVCItems, restoreVCItems
+from nonversioned import getNonVersionedData, restoreNonVersionedData
class Repository(Implicit, Persistent):
@@ -66,12 +66,12 @@
def replaceState(self, obj, new_state):
"""Internal: replace the state of a persistent object.
"""
- subitems = getVCItems(obj)
+ non_versioned = getNonVersionedData(obj)
# XXX There ought to be some way to do this more cleanly.
# This fills the __dict__ of the old object with new state.
- # This replace the object in its container, but applications
- # that use version control turned out much simpler when the
- # identity of the object was retained.
+ # The other way to achieve the desired effect is to replace
+ # the object in its container, but this method preserves the
+ # identity of the object.
if obj.__class__ is not new_state.__class__:
raise VersionControlError(
"The class of the versioned object has changed. %s != %s"
@@ -82,8 +82,9 @@
del obj.__dict__[key]
for key, value in new_state.__dict__.items():
obj.__dict__[key] = value
- if subitems:
- restoreVCItems(obj, subitems)
+ if non_versioned:
+ # Restore the non-versioned data into the new state.
+ restoreNonVersionedData(obj, non_versioned)
return obj
#####################################################################
=== Products/ZopeVersionControl/Version.py 1.8 => 1.9 ===
--- Products/ZopeVersionControl/Version.py:1.8 Tue May 13 18:12:31 2003
+++ Products/ZopeVersionControl/Version.py Fri Jan 30 13:59:39 2004
@@ -24,7 +24,7 @@
from BTrees.OOBTree import OOBTree
from Utility import VersionControlError
-from container import getVCItems, removeVCItems
+from nonversioned import listNonVersionedObjects, removeNonVersionedData
def cloneByPickle(obj, ignore_list=()):
@@ -97,18 +97,14 @@
security.declarePrivate('stateCopy')
def stateCopy(self, obj, container):
- """Get a deep copy of the state of an object, breaking any database
- identity references."""
- map = getVCItems(obj)
- ignore = []
- if map:
- for k, v in map.items():
- ignore.append(aq_base(v))
+ """Get a deep copy of the state of an object.
+
+ Breaks any database identity references.
+ """
+ ignore = listNonVersionedObjects(obj)
res = cloneByPickle(aq_base(obj), ignore)
- if map:
- removeVCItems(res)
+ removeNonVersionedData(res)
return res
InitializeClass(Version)
-
=== Removed File Products/ZopeVersionControl/container.py ===
More information about the Zope-CVS
mailing list