[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