[Checkins] SVN: z3c.vcsync/trunk/src/z3c/vcsync/ Get rid of IVcLoad and introduce smarter loading method which only loads

Martijn Faassen faassen at infrae.com
Wed Jul 4 18:51:59 EDT 2007


Log message for revision 77438:
  Get rid of IVcLoad and introduce smarter loading method which only loads
  that which has changed.
  

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/tests.py
  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	2007-07-04 21:56:59 UTC (rev 77437)
+++ z3c.vcsync/trunk/src/z3c/vcsync/README.txt	2007-07-04 22:51:59 UTC (rev 77438)
@@ -387,22 +387,28 @@
   >>> grok.grok_component('ItemFactory', ItemFactory)
   True
 
-Now for containers. They are registered for an empty extension. They
-are also required to use VcLoad to load their contents::
+Now for containers. They are registered for an empty extension::
 
-  >>> from z3c.vcsync.interfaces import IVcLoad
   >>> class ContainerFactory(grok.GlobalUtility):
   ...   grok.provides(IVcFactory)
   ...   def __call__(self, checkout, path):
   ...       container = Container()
-  ...       IVcLoad(container).load(checkout, path)
   ...       return container
   >>> grok.grok_component('ContainerFactory', ContainerFactory)
   True
 
-We have registered enough. Let's load up the contents from the
-filesystem now::
+We need to maintain a list of everything modified, added or deleted by
+the update operation. Normally this information is extracted from the
+version control system, but for the purposes of this test we maintain
+it manually. In this case, everything is added::
 
+  >>> checkout._added = [root.join('foo.test'), root.join('hoi.test'),
+  ...   root.join('sub'), root.join('sub', 'qux.test')]
+  >>> checkout._deleted = []
+  >>> checkout._modified = []
+
+Let's load up the contents from the filesystem now::
+
   >>> container2 = Container()
   >>> container2.__name__ = 'root'
   >>> checkout.load(container2)
@@ -444,8 +450,16 @@
   ...    hoi_path.write('200\n')
   >>> checkout.update_function = update_function
 
+Now let's do an update::
+
   >>> checkout.up()
 
+We maintain the lists of things changed::
+
+  >>> checkout._added = []
+  >>> checkout._deleted = []
+  >>> checkout._modified = [hoi_path]
+
 We will reload the checkout into Python objects::
 
   >>> checkout.load(container2)
@@ -468,6 +482,12 @@
 
   >>> checkout.up()
 
+We maintain the lists of things changed::
+
+  >>> checkout._added = [hallo]
+  >>> checkout._deleted = []
+  >>> checkout._modified = []
+
 We will reload the checkout into Python objects again::
 
   >>> checkout.load(container2)
@@ -488,6 +508,12 @@
 
   >>> checkout.up()
 
+We maintain the lists of things changed::
+
+  >>> checkout._added = []
+  >>> checkout._deleted = [hallo]
+  >>> checkout._modified = []
+
 We will reload the checkout into Python objects::
 
   >>> checkout.load(container2)
@@ -512,6 +538,12 @@
   
   >>> checkout.up()
 
+We maintain the lists of things changed::
+
+  >>> checkout._added = [newdir_path, newdir_path.join('newfile.test')]
+  >>> checkout._deleted = []
+  >>> checkout._modified = []
+
 Reloading this will cause a new container to exist::
 
   >>> checkout.load(container2)
@@ -534,6 +566,14 @@
 
   >>> checkout.up()
 
+We maintain the lists of things changed::
+
+  >>> checkout._added = []
+  >>> checkout._deleted = [newdir_path, newdir_path.join('newfile.test')]
+  >>> checkout._modified = []
+
+And reload the data::
+
   >>> checkout.load(container2)
 
 Reloading this will cause the new container to be gone again::
@@ -556,6 +596,12 @@
   ...   some_path.write('1000\n')
   >>> checkout.update_function = update_function
 
+We maintain the lists of things changed::
+
+  >>> checkout._added = [hoi_path2, hoi_path2.join('some.test')]
+  >>> checkout._deleted = [hoi_path]
+  >>> checkout._modified = []
+
   >>> checkout.up()
 
 Reloading this will cause a new container to be there instead of the file::
@@ -580,6 +626,12 @@
 
   >>> checkout.up()
 
+We maintain the lists of things changed::
+
+  >>> checkout._added = [hoi_path]
+  >>> checkout._deleted = [hoi_path2.join('some.test'), hoi_path2]
+  >>> checkout._modified = []
+
 Reloading this will cause a new item to be there instead of the
 container::
 
@@ -600,11 +652,17 @@
 Next, we willl add a new ``alpha`` file to the checkout when we do an
 ``up()``, so again we simulate the actions of our version control system::
 
+  >>> alpha_path = root.join('alpha.test').ensure()
   >>> def update_function():
-  ...   alpha_path = root.join('alpha.test').ensure()
   ...   alpha_path.write('4000\n')
   >>> checkout.update_function = update_function
 
+We maintain the lists of things changed::
+
+  >>> checkout._added = [alpha_path]
+  >>> checkout._deleted = []
+  >>> checkout._modified = []
+
 Now we'll synchronize with the memory structure::
 
   >>> state = TestState(container2)

Modified: z3c.vcsync/trunk/src/z3c/vcsync/interfaces.py
===================================================================
--- z3c.vcsync/trunk/src/z3c/vcsync/interfaces.py	2007-07-04 21:56:59 UTC (rev 77437)
+++ z3c.vcsync/trunk/src/z3c/vcsync/interfaces.py	2007-07-04 22:51:59 UTC (rev 77438)
@@ -14,11 +14,6 @@
         Returns the path just created.
         """
 
-class IVcLoad(Interface):
-    def load(checkout, path):
-        """Load data in checkout's path into context object.
-        """
-
 class ISerializer(Interface):
     def serialize(f):
         """Serialize object to file object.
@@ -37,16 +32,7 @@
     def __call__():
         """Create new instance of object.
         """
-    
-class IModified(Interface):
-    def modified_since(dt):
-        """Return True if the object has been modified since dt.
-        """
 
-    def update():
-        """Update modification datetime.
-        """
-
 class IState(Interface):
     """Information about Python object state.
     """
@@ -101,15 +87,15 @@
         """Commit checkout to version control system.
         """
 
-    def added_by_up():
+    def added():
         """A list of those files that have been added after 'up'.
         """
 
-    def deleted_by_up():
+    def deleted():
         """A list of those files that have been deleted after 'up'.
         """
 
-    def modified_by_up():
+    def modified():
         """A list of those files that have been modified after 'up'.
         """
     

Modified: z3c.vcsync/trunk/src/z3c/vcsync/tests.py
===================================================================
--- z3c.vcsync/trunk/src/z3c/vcsync/tests.py	2007-07-04 21:56:59 UTC (rev 77437)
+++ z3c.vcsync/trunk/src/z3c/vcsync/tests.py	2007-07-04 22:51:59 UTC (rev 77438)
@@ -10,14 +10,17 @@
 from zope.app.container.interfaces import IContainer
 from zope.exceptions.interfaces import DuplicationError
 
-from z3c.vcsync.interfaces import ISerializer, IVcDump, IVcLoad, IVcFactory, IModified
+from z3c.vcsync.interfaces import ISerializer, IVcDump, IVcFactory
 from z3c.vcsync import vc
 
 class TestCheckout(vc.CheckoutBase):
     def __init__(self, path):
         super(TestCheckout, self).__init__(path)
         self.update_function = None
-
+        self._added = []
+        self._deleted = []
+        self._modified = []
+        
     def up(self):
         # call update_function which will modify the checkout as might
         # happen in a version control update. Function should be set before
@@ -30,6 +33,15 @@
     def commit(self, message):
         pass
 
+    def added(self):
+        return self._added
+
+    def deleted(self):
+        return self._deleted
+
+    def modified(self):
+        return self._modified
+    
 class TestState(object):
     def __init__(self, root):
         self.root = root

Modified: z3c.vcsync/trunk/src/z3c/vcsync/vc.py
===================================================================
--- z3c.vcsync/trunk/src/z3c/vcsync/vc.py	2007-07-04 21:56:59 UTC (rev 77437)
+++ z3c.vcsync/trunk/src/z3c/vcsync/vc.py	2007-07-04 22:51:59 UTC (rev 77438)
@@ -2,13 +2,11 @@
 from datetime import datetime
 
 from zope.interface import Interface
-from zope.component import queryUtility, queryAdapter
+from zope.component import queryUtility, getUtility, queryAdapter
 from zope.app.container.interfaces import IContainer
 from zope.traversing.interfaces import IPhysicallyLocatable
 
-from z3c.vcsync.interfaces import (IVcDump, IVcLoad,
-                                   ISerializer, IVcFactory,
-                                   IModified, ICheckout)
+from z3c.vcsync.interfaces import IVcDump, ISerializer, IVcFactory, ICheckout
 
 import grok
 
@@ -37,36 +35,33 @@
         path = path.join(self.context.__name__)
         path.ensure(dir=True)
 
-class ContainerVcLoad(grok.Adapter):
-    grok.provides(IVcLoad)
-    grok.context(IContainer)
-    
-    def load(self, checkout, path):
-        loaded = []
-        for sub in path.listdir():
-            if sub.basename.startswith('.'):
-                continue
-            if sub.check(dir=True):
-                object_name = '' # containers are indicated by empty string
-            else:
-                object_name = sub.ext
-            factory = queryUtility(IVcFactory, name=object_name, default=None)
-            # we cannot handle this kind of object, so skip it
-            if factory is None:
-                continue
-            # create instance of object and put it into the container
-            # XXX what if object is already there?
-            obj = factory(checkout, sub)
-            # store the newly created object into the container
-            if sub.purebasename in self.context:
-                del self.context[sub.purebasename]
-            self.context[sub.purebasename] = obj
-            loaded.append(sub.purebasename)
-        # remove any objects not there anymore
-        for name in list(self.context.keys()):
-            if name not in loaded:
-                del self.context[name]
+def resolve(root, root_path, path):
+    rel_path = path.relto(root_path)
+    steps = rel_path.split('/')
+    steps = [step for step in steps if step != '']
+    steps = steps[1:]
+    obj = root
+    for step in steps:
+        name, ex = os.path.splitext(step)
+        try:
+            obj = obj[name]
+        except KeyError:
+            return None
+    return obj
 
+def resolve_container(root, root_path, path):
+    rel_path = path.relto(root_path)
+    steps = rel_path.split('/')
+    steps = [step for step in steps if step != '']
+    steps = steps[1:-1]
+    obj = root
+    for step in steps:
+        try:
+            obj = obj[step]
+        except KeyError:
+            return None
+    return obj
+
 class CheckoutBase(object):
     """Checkout base class.
 
@@ -120,12 +115,24 @@
                                self.get_container_path(root, obj))
 
     def load(self, object):
-        # XXX can only load containers here, not items
-        names = [path.purebasename for path in self.path.listdir()
-                 if not path.purebasename.startswith('.')]
-        assert len(names) == 1
-        IVcLoad(object).load(self, self.path.join(names[0]))
-        
+        root = object
+        for deleted_path in self.deleted():
+            obj = resolve(root, self.path, deleted_path)
+            if obj is not None:
+                del obj.__parent__[obj.__name__]
+        added_paths = self.added()
+        # to ensure that containers are created before items we sort them
+        sorted(added_paths)
+        for added_path in added_paths:
+            obj = resolve_container(root, self.path, added_path)
+            factory = getUtility(IVcFactory, name=added_path.ext)
+            obj[added_path.purebasename] = factory(self, added_path)
+        for modified_path in self.modified():
+            obj = resolve(root, self.path, modified_path)
+            factory = getUtility(IVcFactory, name=modified_path.ext)
+            del obj.__parent__[obj.__name__]
+            obj.__parent__[obj.__name__] = factory(self, modified_path)
+                
     def up(self):
         raise NotImplementedError
 
@@ -135,19 +142,11 @@
     def commit(self, message):
         raise NotImplementedError
 
-    def added_by_up(self):
+    def added(self):
         raise NotImplementedError
 
-    def deleted_by_up(self):
+    def deleted(self):
         raise NotImplementedError
 
-    def modified_by_up(self):
+    def modified(self):
         raise NotImplementedError
-
-class ContainerModified(grok.Adapter):
-    grok.provides(IModified)
-    grok.context(IContainer)
-
-    def modified_since(self, dt):
-        # containers themselves are never modified
-        return False



More information about the Checkins mailing list