[Zope-CVS] SVN: zversioning/trunk/src/versioning/ setup basic test of IVersionableAspect adapter

Uwe Oestermeier uwe_oestermeier at iwm-kmrc.de
Tue Oct 12 13:34:09 EDT 2004


Log message for revision 28018:
  setup basic test of IVersionableAspect adapter


Changed:
  U   zversioning/trunk/src/versioning/interfaces.py
  U   zversioning/trunk/src/versioning/storage.py
  U   zversioning/trunk/src/versioning/tests/README.txt
  U   zversioning/trunk/src/versioning/tests/repository_setup.py


-=-
Modified: zversioning/trunk/src/versioning/interfaces.py
===================================================================
--- zversioning/trunk/src/versioning/interfaces.py	2004-10-12 17:30:38 UTC (rev 28017)
+++ zversioning/trunk/src/versioning/interfaces.py	2004-10-12 17:34:06 UTC (rev 28018)
@@ -35,6 +35,11 @@
 used backend generates its own versions.
 """
 
+import persistent, zope
+from zope.interface import Interface
+
+
+
 class IRepository(Interface):
     """A version repository providing core functionality.
     
@@ -136,7 +141,7 @@
         """Returns the metadata of all versions of the given object.
         """
 
-class IDeletableStorage(IStorage) :
+class IDeletableRepository(Interface) :
     """ Most versioning systems do not allow to throw away versioned
     data, but there might be use cases were simple file repositories
     or other storage solutions can sweep out old versions. """
@@ -149,7 +154,7 @@
         """
 
 
-class ICheckoutAware(Interfaces):
+class ICheckoutAware(Interface):
     """Marking objects as checked in or checked out.
     
     XXX Naming conventions? Aren't IBlahAware interfaces usually marker 

Modified: zversioning/trunk/src/versioning/storage.py
===================================================================
--- zversioning/trunk/src/versioning/storage.py	2004-10-12 17:30:38 UTC (rev 28017)
+++ zversioning/trunk/src/versioning/storage.py	2004-10-12 17:34:06 UTC (rev 28018)
@@ -12,112 +12,77 @@
 #
 ##############################################################################
 
+import unittest, doctest
 
-from datetime import datetime
-  
-import persistent
-from BTrees.OOBTree import OOBTree
-from BTrees.OIBTree import OIBTree
+from zope.interface import implements
+from zope.app.copypastemove.interfaces import IObjectCopier 
+from zope.app.folder import Folder
+from zope.app.container.interfaces import INameChooser
+from zope.app.exception.interfaces import UserError
 
-class TemporalParadox(Exception): pass
-  
-  
-class Version(object) :
-    """ Contains a pointer to the versioned data as well
-        as some metadata.
-    """
-   
-    implents(IVersion)
+
+class SingleHistory(Folder) :
+
+    implements(INameChooser)
     
-    def __init__(self, data, metadata) :
-        self.data = data
-        self.when = datetime.datetime(metadata["time"])
-        self.who = metadata["user_name"]
-        self.description = metadata["description"]
-    
-    
-class HistoryStorageMixin(object) :
+    def checkName(self, name, object):
+        """Check whether an object name is valid.
 
-    _histories = None
+        Raise a user error if the name is not valid.
+        """
+        raise UserError("versions cannot be named by the user.")
+        
 
-    def getTicket(self, obj) :
-        raise NotImplementedError
+    def chooseName(self, name, object):
+        """Choose a unique valid name for the object
 
-    def getHistory(self, obj) :
-        """ Returns the history for a versionable object. """  
-        ticket = self.getTicket()
-        if self.hasHistory(
-         
-    def load(self, obj, selector) :
+        The given name and object may be taken into account when
+        choosing the name.
+
+        """
+        return "Version %03d" % (len(self)+1)
         
-   
-    def save(self, obj) :
     
-  
-   
-      
-class SimpleHistoryStorage(Persistent) :
-    """ We simply use the ZODB history for our references to
-        revisions of an object.
-      
-    """
+        
+         
     
-    def __init__(self) :
-        self._histories = OIBTree()
-    
-    def getTicket(self, obj) :
-        return obj._p_oid
+         
+class SimpleHistoryStorage(Folder) :
+    """
+        Implements the probably most simple way of version control in Zope3.
+        It uses the following existing Zope mechanisms :
             
-    
+           the _p_oid as an identification ticket for objects and their versions
+           a Folder as a container for histories were each History is itself a Folder
+           
+        >>> from policies import VersionableAspectsAdapter
+        >>> from zope.app.tests.setup import buildSampleFolderTree
+        >>> sample = buildSampleFolderTree()
+        >>> histories = SimpleHistoryStorage()
+        >>> sample.keys()
         
+    """
+   
+
+    def getTicket(self, obj) :
+        return str(obj._p_oid)
+ 
+    def register(self, obj):
+        """ Register an obj for version control.
+            Creates a new version history for a resource."""
+        history = SingleHistory()
+        ticket = self.getTicket(obj)
+        self[ticket] = history
+        return ticket
         
-# 
-# 
-# class HystoryJar(object) :
-#     """A ZODB Connection-like object that provides access to data
-#     but prevents history from being changed.
-#     
-#     Shamelessly copied from Zope2 OFS.History and thus ported to Zope3
-#   
-#     XXX is there something in Zope3 that already corresponds to this.
-#     """
-# 
-#     def __init__(self, base):
-#         self.__base__=base
-# 
-#     def __getattr__(self, name):
-#         return getattr(self.__base__, name)
-# 
-#     def commit(self, object, transaction):
-#         if object._p_changed:
-#             raise TemporalParadox, "You can't change history!"
-# 
-#     def abort(*args, **kw): pass
-# 
-#     tpc_begin = tpc_finish = abort
-#     
-#          
-#     def _getVersionFromHistory(self, ticket, revision):
-#         """ Retrieves a revision from a history of an object. """
-#         
-#         serial=revision['serial']
-#         state=ticket._p_jar.oldstate(self, serial)
-#         rev=ticket.__class__.__basicnew__()
-#         rev._p_jar=HystoryJar(ticket._p_jar)
-#         rev._p_oid=ticket._p_oid
-#         rev._p_serial=serial
-#         rev.__setstate__(state)
-#         rev._p_changed=0
-#        
-#         return IVersion(rev, revision)
-#         
-#     def getVersions(obj):
-#         """Get a list of versions."""
-#         revisions = obj._p_jar.db().history(self._p_oid, None, 20)
-#         if revisions is None:
-#             return ()
-#         return [self._getVersionFromHistory(r) for r in revisions]
-#         
+    def getHistory(self, obj):
+        """Internal: return a version history given a version history id."""
+        ticket = self.getTicket(obj)
+        return self[ticket]
+        
+  
+             
+   
 
 
 

Modified: zversioning/trunk/src/versioning/tests/README.txt
===================================================================
--- zversioning/trunk/src/versioning/tests/README.txt	2004-10-12 17:30:38 UTC (rev 28017)
+++ zversioning/trunk/src/versioning/tests/README.txt	2004-10-12 17:34:06 UTC (rev 28018)
@@ -95,134 +95,54 @@
   >>> new_b["c"] == new_c
   False
   
-as well as the reference from c to a is defunct :
+as well as the reference from c to a :
   
   >>> new_c.refers_to == new_a
   False
-  
-A closer look reveals that
 
-  >>> new_c         # doctest: +ELLIPSIS
-  <zope.app.versioncontrol.README.TestFolder object at ...>
-
   
-This demonstrates that the reference to a is not correctly preserved. To
-achieve this goal we overwrite the copy process with our own:
+This demonstrates that the reference to a is not preserved, which is the major
+motivation for a new implementation.
 
-    
-  >>> def cloneByPickle(obj, repository, 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
-  ...     ids = {"ignored": object()}
-  ...     
-  ...     def persistent_id(ob):
-  ...         if ignore_dict.has_key(id(ob)):
-  ...             return 'ignored'
-  ...         if IVersionable.providedBy(object) :
-  ...             if IVersioned.providedBy(object) :
-  ...                myid = repository.getExistingTicket(object)
-  ...             else :
-  ...                myid = repository.getNewTicket(object)
-  ...            
-  ...             ids[myid] = ob
-  ...             return myid
-  ...         if getattr(ob, '_p_changed', 0) is None:
-  ...             ob._p_changed = 0
-  ...         return None
-  ...     
-  ...     stream = StringIO()
-  ...     p = Pickler(stream, 1)
-  ...     p.persistent_id = persistent_id
-  ...     p.dump(obj)
-  ...     stream.seek(0)
-  ...     u = Unpickler(stream)
-  ...     u.persistent_load = ids.get
-  ...     return u.load()
 
-  >>> VERSION_INFO_KEY = "gaga.at.isarsprint"
-  >>> from zope.app.versioncontrol.repository import Repository
-  >>> from zope.app.uniqueid import UniqueIdUtility
-  >>> class RefertialVersionControl(Repository) : 
-  ...   # an implementation that preprocesses the object states
-  ...
-  ...   tickets = UniqueIdUtility()
-  ...
-  ...   def getExistingTicket(self, object) :
-  ...       IAnnotations(object)[VERSION_INFO_KEY]
-  ...
-  ...   def getNewTicket(self, object) :
-  ...       id = self.tickets.register(object)
-  ...       IAnnotations(object)[VERSION_INFO_KEY] = id
-  ...       return id
-  ...
-  ...   def applyVersionControl(self, object, message=None) :
-  ...       obj = self.preprocess(object)
-  ...       super(RefertialVersionControl, self).applyVersionControl(obj, message)
-  ...
-  ...   def getVersionOfResource(self, history_id, branch) :
-  ...       obj = super(RefertialVersionControl, self).getVersionOfResource(history_id, branch)
-  ...       return self.postprocess(obj)
-  ...
-  ...   def preprocess(self, obj) :
-  ...       # We replace python references by unique ids
-  ...       return obj.cloneByPickle()
-  ...
-  ...   def postprocess(self, obj) :
-  ...       return obj   
-  
-  >>> def declare_unversioned(object):
-  ...   # remove object from version controll
-  ...   ifaces = zope.interface.directlyProvidedBy(object)
-  ...   ifaces -= IVersioned
-  ...   zope.interface.directlyProvides(object, *ifaces)
-    
-  >>> declare_unversioned(sample)
-  >>> declare_unversioned(a)
-  >>> declare_unversioned(b)
-  >>> declare_unversioned(c)
-  >>> repository2 = buildRepository(RefertialVersionControl, interaction=False)
-  >>> repository2.applyVersionControl(sample)
-  >>> repository2.applyVersionControl(a)
-  >>> repository2.applyVersionControl(b)
-  >>> repository2.applyVersionControl(c)
-  
- 
-  >>> new_a = accessVersion(repository2, a)
-  >>> new_b = accessVersion(repository2, b)
-  >>> new_c = accessVersion(repository2, c)
-  
-  Now the reference from b to c is intact:
-  >>> new_b["c"] == new_c
-  False
-  >>> new_c.refers_to == new_a
-  False
-  
 
 
+Alternative implementation
+--------------------------
 
+We want to use versioning with objects other than standard zope objects that use
+only the standard containment structure meachanism. In the same time some parts
+of the versioning system should be pluggable, e.g. the storage for the object histories,
+the locking mechanism etc.
 
+We start with the basic building blocks, a storage that holds version histories
+of several objects. Note that this implementation does not collide with the
+implementation in zope.app.versioncontrol. This versioning scheme does not attach any
+information to the versioned objects and keeps the necessary bookeeping information 
+encapsulated in the storage of object histories.
 
-Extensions: We want to define a repository that works as a black box and returns
-only a ticket which guarantees that we get a valid copy back if we use this ticket.
+    >>> from versioning.storage import SimpleHistoryStorage
+    >>> from versioning.policies import VersionableAspectsAdapter
+    >>> histories = SimpleHistoryStorage()
+    >>> histories.register(a)
+    '\x00\x00\x00\x00\x00\x00\x00\x04'
+    >>> histories.register(b)
+    '\x00\x00\x00\x00\x00\x00\x00\x05'
+    >>> util.commit()
+    >>> len(histories.values())
+    2
+    >>> [x for x in histories.keys()]
+    [u'\x00\x00\x00\x00\x00\x00\x00\x04', u'\x00\x00\x00\x00\x00\x00\x00\x05']
+    >>> adapter = VersionableAspectsAdapter(a, histories)
+    >>> adapter.writeAspects()
+    'Version 001'
 
-class IRepository(Interface) :
 
-    def register(self, obj) :
-        """ Returns an ITicket. """
-        
-    def retrieve(self, ticket) :
-        """ Returns an object or throws an ObjectNotFound 
-            or ObjectPermanentylDeleted exception.
-        """
+
     
-We want to use versioning with object other than standard zope objects that use
-only the standard containment structure meachanism.
 
 
 
+
+
+

Modified: zversioning/trunk/src/versioning/tests/repository_setup.py
===================================================================
--- zversioning/trunk/src/versioning/tests/repository_setup.py	2004-10-12 17:30:38 UTC (rev 28017)
+++ zversioning/trunk/src/versioning/tests/repository_setup.py	2004-10-12 17:34:06 UTC (rev 28018)
@@ -29,7 +29,14 @@
 from zope.app.file.file import File
 from zope.app.folder.folder import Folder
 
+from zope.app.copypastemove.interfaces import IObjectCopier
+from zope.app.copypastemove import ObjectCopier
 
+
+from zope.app.container.interfaces import IWriteContainer, INameChooser
+from zope.app.container.contained import NameChooser
+
+
 def registerAdapter() :
     """ Register some common adapter. """ 
 
@@ -43,8 +50,11 @@
     ztapi.provideAdapter(IContainmentRoot, IPhysicallyLocatable, RootPhysicallyLocatable)
     ztapi.provideAdapter(IAnnotatable, IZopeDublinCore, ZDCAnnotatableAdapter)
 
-
-
+    # for copy and moves
+    ztapi.provideAdapter(None, IObjectCopier, ObjectCopier)
+    ztapi.provideAdapter(IWriteContainer, INameChooser, NameChooser)
+    
+    
 def buildDatabaseRoot():
     """Opens a connection to a test database and returns the root object
     """



More information about the Zope-CVS mailing list