[Checkins] SVN: z3c.vcsync/trunk/src/z3c/vcsync/ Make the state track revision numbers to simply the API for sync().

Martijn Faassen faassen at infrae.com
Tue Apr 22 11:04:54 EDT 2008


Log message for revision 85598:
  Make the state track revision numbers to simply the API for sync().
  

Changed:
  U   z3c.vcsync/trunk/src/z3c/vcsync/README.txt
  U   z3c.vcsync/trunk/src/z3c/vcsync/interfaces.py
  U   z3c.vcsync/trunk/src/z3c/vcsync/internal.txt
  U   z3c.vcsync/trunk/src/z3c/vcsync/vc.py

-=-
Modified: z3c.vcsync/trunk/src/z3c/vcsync/README.txt
===================================================================
--- z3c.vcsync/trunk/src/z3c/vcsync/README.txt	2008-04-22 13:45:55 UTC (rev 85597)
+++ z3c.vcsync/trunk/src/z3c/vcsync/README.txt	2008-04-22 15:04:53 UTC (rev 85598)
@@ -45,52 +45,19 @@
 files that have been changed, added or removed on the filesystem due
 to the ``up`` action will change the persistent object state.
 
-The tree to synchronize
------------------------
+State
+-----
+ 
+Content to synchronize is represented by an object that provides
+``IState``. The following methods need to be implemented:
 
-Content objects need to track the revision number after which it was
-last changed, so that later we can find all objects that have
-changed. In a real application we would typically track this in some
-content object (such as the application root), but here we will just
-track it globally::
+* ``get_revision_nr()``: return the last revision number that the
+   application was synchronized with. The state typically stores this
+   the application object.
 
-  >>> last_revision_nr = 0
+* ``set_revision_nr(nr)``: store the last revision number that the
+  application was synchronized with.
 
-An item contains some payload data, and maintains the SVN revision
-after which it was changed. In a real program you would typically
-maintain the revision number of objects by using an annotation and
-listening to ``IObjectModifiedEvent``, but we will use a property
-here::
-
-  >>> class Item(object):
-  ...   def __init__(self, payload):
-  ...     self.payload = payload
-  ...   def _get_payload(self):
-  ...     return self._payload
-  ...   def _set_payload(self, value):
-  ...     self._payload = value
-  ...     self.revision_nr = last_revision_nr
-  ...   payload = property(_get_payload, _set_payload)
-
-We also have a ``Container`` class, set up before this test
-started. It is a class that implements enough of the dictionary API
-and implements the ``IContainer`` interface. A normal Zope 3 folder or
-Grok container will work. Let's now set up the tree::
-
-  >>> data = Container()
-  >>> data.__name__ = 'root'
-  >>> data['foo'] = Item(payload=1)
-  >>> data['bar'] = Item(payload=2)
-  >>> data['sub'] = Container()
-  >>> data['sub']['qux'] = Item(payload=3)
-
-As part of the synchronization procedure we need the ability to export
-persistent python objects to the version control checkout directory in
-the form of files and directories.
- 
-Content is represented by an object that provides ``IState``. Two methods
-need to be implemented:
-
 * ``objects(revision_nr)``: any object that has been modified (or
   added) since the synchronization for ``revision_nr``. Returning 'too
   many' objects (objects that weren't modified) is safe, though less
@@ -113,7 +80,8 @@
   list.
 
 In this example, we will use a simpler, less efficient, implementation
-that goes through the entire tree to find changes::
+that goes through a content to find changes. It tracks the
+revision number as a special attribute of the root object::
 
   >>> from zope.interface import implements
   >>> from z3c.vcsync.interfaces import IState
@@ -121,6 +89,13 @@
   ...     implements(IState)
   ...     def __init__(self, root):
   ...         self.root = root
+  ...     def set_revision_nr(self, nr):
+  ...         self.root.nr = nr
+  ...     def get_revision_nr(self):
+  ...         try:
+  ...             return self.root.nr
+  ...         except AttributeError:
+  ...             return 0
   ...     def removed(self, revision_nr):
   ...         return []
   ...     def objects(self, revision_nr):
@@ -140,6 +115,53 @@
   ...             for sub_container in self._containers_helper(obj):
   ...                 yield sub_container
 
+
+The content
+-----------
+
+Now that we have something that can synchronize a tree of content in
+containers, let's actually build ourselves a tree of content.
+
+An item contains some payload data, and maintains the SVN revision
+after which it was changed. In a real application you would typically
+maintain the revision number of objects by using an annotation and
+listening to ``IObjectModifiedEvent``, but we will use a property
+here::
+
+  >>> class Item(object):
+  ...   def __init__(self, payload):
+  ...     self.payload = payload
+  ...   def _get_payload(self):
+  ...     return self._payload
+  ...   def _set_payload(self, value):
+  ...     self._payload = value
+  ...     self.revision_nr = get_revision_nr()
+  ...   payload = property(_get_payload, _set_payload)
+
+This code needs a global ``get_revision_nr`` function available to get access
+to the revision number of last synchronization. For now we'll just define
+this to return 0, but we will change this later::
+
+  >>> def get_revision_nr():
+  ...    return 0
+
+Besides the ``Item`` class, we also have a ``Container`` class, set up
+before this test started. It is a class that implements enough of the
+dictionary API and implements the ``IContainer`` interface. A normal
+Zope 3 folder or Grok container will also work. Let's now set up the
+tree::
+
+  >>> data = Container()
+  >>> data.__name__ = 'root'
+  >>> data['foo'] = Item(payload=1)
+  >>> data['bar'] = Item(payload=2)
+  >>> data['sub'] = Container()
+  >>> data['sub']['qux'] = Item(payload=3)
+
+As part of the synchronization procedure we need the ability to export
+persistent python objects to the version control checkout directory in
+the form of files and directories.
+
 Now that we have an implementation of ``IState`` that works for our
 state, let's create our ``state`` object::
 
@@ -251,31 +273,42 @@
   >>> from z3c.vcsync import Synchronizer
   >>> s = Synchronizer(checkout, state)
 
+Let's make ``s`` the current synchronizer as well. We need this in
+this example to get back to the last revision number::
+
+  >>> current_synchronizer = s
+
+It's now time to set up our ``get_revision_nr`` function a bit better,
+making use of the information in the current synchronizer. In actual
+applications we'd probably get the revision number directly from the
+content, and there would be no need to get back to the synchronizer
+(it doesn't need to be persistent but can be constructed on demand)::
+
+  >>> def get_revision_nr():
+  ...    return current_synchronizer.state.get_revision_nr()
+
 Synchronization
 ---------------
 
 We'll synchronize for the first time now::
 
-  >>> info = s.sync(last_revision_nr, "synchronize")
+  >>> info = s.sync("synchronize")
 
-We can now update the last_revision_nr with the revision number we
-have just synchronized to::
-
-  >>> last_revision_nr = info.revision_nr
-
 We will now examine the SVN checkout to see whether the
-synchronization was success.
+synchronization was successful.
 
-We first introduce some helper functions that help us present the
-paths in a more readable form, relative to the base of the checkout::
+To do this we'll introduce some helper functions that help us present
+the paths in a more readable form, relative to the base of the
+checkout::
 
   >>> def pretty_path(path):
   ...     return path.relto(wc)
   >>> def pretty_paths(paths):
   ...     return sorted([pretty_path(path) for path in paths])
 
-We see that the structure containers and items  has been translated to the
-same structure of directories and ``.test`` files on the filesystem::
+We see that the Python object structure of containers and items has
+been translated to the same structure of directories and ``.test``
+files on the filesystem::
 
   >>> pretty_paths(wc.listdir())
   ['root']
@@ -299,15 +332,8 @@
 Let's now try the reverse: we will change the SVN content from another
 checkout, and synchronize the changes back into the object tree.
 
-We will store away last_revision_nr for a while, as we will now
-temporarily work with another checkout and state, which track a
-different revision_nr::
+We have a second, empty tree that we will load objects into::
 
-  >>> last_revision_nr1 = last_revision_nr
-
-We have a second, empty tree that we will load objects into. This
-state hasn't synchronized since the revision number was 0::
-
   >>> last_revision_nr = 0
   >>> data2 = Container()
   >>> data2.__name__ = 'root'
@@ -321,12 +347,19 @@
   >>> wc2.checkout(repo)
   >>> checkout2 = SvnCheckout(wc2)
 
-Let's synchronize::
+Let's make a synchronizer for this new checkout and state::
 
   >>> s2 = Synchronizer(checkout2, state2)
-  >>> info2 = s2.sync(last_revision_nr, "synchronize")
-  >>> last_revision_nr = info2.revision_nr
 
+This is now the current synchronizer (so that our ``get_revision_nr``
+works properly)::
+
+  >>> current_synchronizer = s2
+
+Now we'll synchronize::
+
+  >>> info = s2.sync("synchronize")
+
 The state of objects in the tree must now mirror that of the original state::
 
   >>> sorted(data2.keys())    
@@ -336,15 +369,12 @@
 
   >>> data2['bar'].payload = 20
   >>> data2['sub']['qux'].payload = 30
-  >>> info2 = s2.sync(last_revision_nr, "synchronize")
-  >>> last_revision_nr = info2.revision_nr
+  >>> info2 = s2.sync("synchronize")
 
 We can now synchronize the original tree again::
 
-  >>> last_revision_nr2 = last_revision_nr
-  >>> last_revision_nr = last_revision_nr1
-  >>> info = s.sync(last_revision_nr, "synchronize")
-  >>> last_revision_nr = info.revision_nr
+  >>> current_synchronizer = s
+  >>> info = s.sync("synchronize")
 
 We should see the changes reflected into the original tree::
 

Modified: z3c.vcsync/trunk/src/z3c/vcsync/interfaces.py
===================================================================
--- z3c.vcsync/trunk/src/z3c/vcsync/interfaces.py	2008-04-22 13:45:55 UTC (rev 85597)
+++ z3c.vcsync/trunk/src/z3c/vcsync/interfaces.py	2008-04-22 15:04:53 UTC (rev 85598)
@@ -56,13 +56,23 @@
     This is object represents the state and contains information about
     what objects in the state have been changed/added, or
     removed. This information is used to determine what to synchronize
-    to the filesystem.
+    to the filesystem. It also keeps track of the last revision
+    number used to synchronize.
     
     Implement this for the state structure (such as a container tree)
     you want to export.
     """
     root = Attribute('The root container')
 
+    def set_revision_rr():
+        """The last revision number that this state was synchronized with.
+        """
+
+    def set_revision_rr(nr):
+        """Store the last revision number that this state was synchronized
+        with.
+        """
+
     def objects(revision_nr):
         """Objects modified/added in state since revision_nr.
 

Modified: z3c.vcsync/trunk/src/z3c/vcsync/internal.txt
===================================================================
--- z3c.vcsync/trunk/src/z3c/vcsync/internal.txt	2008-04-22 13:45:55 UTC (rev 85597)
+++ z3c.vcsync/trunk/src/z3c/vcsync/internal.txt	2008-04-22 15:04:53 UTC (rev 85598)
@@ -700,7 +700,7 @@
 
   >>> def f(obj):
   ...   print "modified:", obj.__name__
-  >>> info = s.sync(None, message='', modified_function=f)
+  >>> info = s.sync(message='', modified_function=f)
   modified: alpha
   >>> info.revision_nr
   9

Modified: z3c.vcsync/trunk/src/z3c/vcsync/vc.py
===================================================================
--- z3c.vcsync/trunk/src/z3c/vcsync/vc.py	2008-04-22 13:45:55 UTC (rev 85597)
+++ z3c.vcsync/trunk/src/z3c/vcsync/vc.py	2008-04-22 15:04:53 UTC (rev 85598)
@@ -105,7 +105,8 @@
         self.state = state
         self._to_remove = []
 
-    def sync(self, revision_nr, message='', modified_function=None):
+    def sync(self, message='', modified_function=None):
+        revision_nr = self.state.get_revision_nr()
         # store these to report in SynchronizationInfo below
         objects_removed = list(self.state.removed(revision_nr))
         root = self.state.root
@@ -137,7 +138,14 @@
                 modified_function(obj)
         # and commit the checkout state
         self.checkout.commit(message)
-        return SynchronizationInfo(self.checkout.revision_nr(),
+
+        # we retrieve the revision number to which we just synchronized
+        revision_nr = self.checkout.revision_nr()
+        
+        # we store the new revision number in the state
+        self.state.set_revision_nr(revision_nr)
+        # and we return some informatino about happened
+        return SynchronizationInfo(revision_nr,
                                    objects_removed, objects_changed,
                                    files_removed, files_changed)
 
@@ -225,18 +233,26 @@
         return self.checkout.path.join(*steps)
 
 class AllState(object):
-    """A special state object.
-
+    """Report all state as changed.
+    
     It reports all objects in the state as modified, and reports nothing
-    removed.
+    removed. It actually completely ignores revision numbers. This
+    implementation is not something you'd typically want to use in your
+    own applications, but is useful for testing purposes.
     """
     grok.implements(IState)
 
     def __init__(self, root):
         self.root = root
 
+    def set_revision_nr(self, revision_nr):
+        pass
+
+    def get_revision_nr(self):
+        return 0
+    
     def objects(self, revision_nr):
-        for container in self._containers(revision_nr):
+        for container in self._containers():
             for item in container.values():
                 if not IContainer.providedBy(item):
                     yield item
@@ -245,7 +261,7 @@
     def removed(self, revision_nr):
         return []
     
-    def _containers(self, revision_nr):
+    def _containers(self):
         return self._containers_helper(self.root)
 
     def _containers_helper(self, container):



More information about the Checkins mailing list