Rev 5100: Create an update policy. These are called when changes occur. in http://bzr.arbash-meinel.com/branches/bzr/lp/2.2.0b2-contained-pack

John Arbash Meinel john at arbash-meinel.com
Mon Mar 8 21:15:16 GMT 2010


At http://bzr.arbash-meinel.com/branches/bzr/lp/2.2.0b2-contained-pack

------------------------------------------------------------
revno: 5100
revision-id: john at arbash-meinel.com-20100308211511-jmk25089ymoiwcl8
parent: john at arbash-meinel.com-20100308200153-ob1otaacxdq09gzb
committer: John Arbash Meinel <john at arbash-meinel.com>
branch nick: 2.2.0b2-contained-pack
timestamp: Mon 2010-03-08 15:15:11 -0600
message:
  Create an update policy. These are called when changes occur.
  
  Note that this still sees 'modify' as an remove+add pair.
-------------- next part --------------
=== modified file 'bzrlib/pack_collection.py'
--- a/bzrlib/pack_collection.py	2010-03-08 20:01:53 +0000
+++ b/bzrlib/pack_collection.py	2010-03-08 21:15:11 +0000
@@ -192,49 +192,27 @@
         raise NotImplementedError(self.write_index)
 
 
-class GraphIndexPolicy(IndexPolicy):
-    """An IndexPolicy that uses a GraphIndex at a particular location."""
-
-    def __init__(self, transport, filename, builder_class, index_class):
-        self.transport = transport
-        self.filename = filename
-        self._index_builder_class = builder_class
-        self._index_class = index_class
-
-    def _do_handle_missing_file(self):
-        """What should we do if the file is not present when we want to read?
-
-        By default, we do nothing, but a child can chose to raise an exception
-        (meaning the file must always exist.)
-        """
-
-    def _do_get_file_mode(self):
-        """Figure out what file mode we should use.
-
-        Default is to just use 'None' which uses OS defaults.
-        """
-        return None
-
-    def read_index(self):
-        """See MemoTrackerPolicy.read_index"""
-        try:
-            index = self._index_class(self.transport, self.filename, size=None)
-            index.key_count() # Trigger reading the header
-        except errors.NoSuchFile, e:
-            self._do_handle_missing_file()
-            # if _do_handle_missing_file doesn't raise, then we just return
-            # nothing, since there is nothing on disk to iterate.
-            return []
-        return [(k[0], v) for _, k, v in index.iter_all_entries()]
-
-    def write_index(self, memos):
-        """See MemoTrackerPolicy.write_index"""
-        builder = self._index_builder_class(reference_lists=0,
-                                            key_elements=1)
-        for name, value in memos:
-            builder.add_node((name,), value)
-        self.transport.put_file(self.filename, builder.finish(),
-                                mode=self._do_get_file_mode())
+class UpdatePolicy(object):
+    """What should we do when data is updated."""
+
+    def memo_removed(self, name, value):
+        """Called when one of the entries is removed.
+        
+        :param name: The name of the memo that was removed
+        :param value: The value for this name
+        """
+        # TODO: Do we need to know if this is a disk change (from save/reload
+        #       noticing an update) versus a memory change (from calling code
+        #       updating the info?)
+        raise NotImplementedError(self.memo_removed)
+
+    def memo_added(self, name, value):
+        """Called when one of the entries is removed.
+        
+        :param name: The name of the memo that was removed
+        :param value: The value for this name
+        """
+        raise NotImplementedError(self.memo_added)
 
 
 class MemoTracker(object):
@@ -255,9 +233,10 @@
         not yet written to disk.)
     """
 
-    def __init__(self, locking_policy, index_policy):
+    def __init__(self, locking_policy, index_policy, update_policy):
         self.locking_policy = locking_policy
         self.index_policy = index_policy
+        self.update_policy = update_policy
         self._memos = None
         # The set of memos() that were on disk when we read it last
         self._last_read_memos = None
@@ -333,6 +312,7 @@
         self._ensure_loaded()
         assert name not in self._memos
         self._memos[name] = value
+        self.update_policy.memo_added(name, value)
 
     def get_memo(self, name):
         """Get the section (index) memo for the given name."""
@@ -347,6 +327,7 @@
         self._ensure_loaded()
         old = self._memos.pop(name)
         assert old == value
+        self.update_policy.memo_removed(name, value)
 
     def save(self):
         """Save the current in-memory content to disk."""
@@ -372,6 +353,59 @@
             self.locking_policy.unlock()
 
 
+class GraphIndexPolicy(IndexPolicy):
+    """An IndexPolicy that uses a GraphIndex at a particular location."""
+
+    def __init__(self, transport, filename, builder_class, index_class):
+        self.transport = transport
+        self.filename = filename
+        self._index_builder_class = builder_class
+        self._index_class = index_class
+
+    def _do_handle_missing_file(self):
+        """What should we do if the file is not present when we want to read?
+
+        By default, we do nothing, but a child can chose to raise an exception
+        (meaning the file must always exist.)
+        """
+
+    def _do_get_file_mode(self):
+        """Figure out what file mode we should use.
+
+        Default is to just use 'None' which uses OS defaults.
+        """
+        return None
+
+    def read_index(self):
+        """See MemoTrackerPolicy.read_index"""
+        try:
+            index = self._index_class(self.transport, self.filename, size=None)
+            index.key_count() # Trigger reading the header
+        except errors.NoSuchFile, e:
+            self._do_handle_missing_file()
+            # if _do_handle_missing_file doesn't raise, then we just return
+            # nothing, since there is nothing on disk to iterate.
+            return []
+        return [(k[0], v) for _, k, v in index.iter_all_entries()]
+
+    def write_index(self, memos):
+        """See MemoTrackerPolicy.write_index"""
+        builder = self._index_builder_class(reference_lists=0,
+                                            key_elements=1)
+        for name, value in memos:
+            builder.add_node((name,), value)
+        self.transport.put_file(self.filename, builder.finish(),
+                                mode=self._do_get_file_mode())
+
+
+class NoopUpdatePolicy(UpdatePolicy):
+
+    def memo_removed(self, name, value):
+        pass
+
+    def memo_added(self, name, value):
+        pass
+
 
 class PackCollection(object):
     """This manages a collection of pack files.

=== modified file 'bzrlib/tests/test_pack_collection.py'
--- a/bzrlib/tests/test_pack_collection.py	2010-03-08 20:01:53 +0000
+++ b/bzrlib/tests/test_pack_collection.py	2010-03-08 21:15:11 +0000
@@ -141,6 +141,18 @@
         return # No lock checking
 
 
+class LoggingUpdatePolicy(pack_collection.UpdatePolicy):
+
+    def __init__(self):
+        self.log = []
+
+    def memo_removed(self, name, value):
+        self.log.append(('removed', name, value))
+
+    def memo_added(self, name, value):
+        self.log.append(('added', name, value))
+
+
 class TestMemoTracker(tests.TestCaseWithMemoryTransport):
 
     _m1 = ('name1', 'content1')
@@ -158,7 +170,8 @@
         tracker = pack_collection.MemoTracker(
             TrivialLockingPolicy(),
             pack_collection.GraphIndexPolicy(self.transport, 'meta',
-                btree_index.BTreeBuilder, btree_index.BTreeGraphIndex))
+                btree_index.BTreeBuilder, btree_index.BTreeGraphIndex),
+            pack_collection.NoopUpdatePolicy())
         return tracker
 
     def assertDiffFromDisk(self, on_disk, active, d_removed, d_added,
@@ -194,6 +207,12 @@
         self.assertEqual([(('name1',), 'content1')],
                  sorted([(n[1], n[2]) for n in btree.iter_all_entries()]))
 
+    def test_add_memo_calls_memo_added(self):
+        policy = LoggingUpdatePolicy()
+        self.tracker.update_policy = policy
+        self.tracker.add_memo(*self._m1)
+        self.assertEqual([('added', 'name1', 'content1')], policy.log)
+
     def test_get_missing_memo(self):
         self.assertRaises(KeyError, self.tracker.get_memo, 'not-there')
 
@@ -218,6 +237,13 @@
         btree = btree_index.BTreeGraphIndex(self.transport, 'meta', size=None)
         self.assertEqual(1, btree.key_count())
 
+    def test_remove_memo_calls_memo_removed(self):
+        policy = LoggingUpdatePolicy()
+        self.tracker.add_memo(*self._m1)
+        self.tracker.update_policy = policy
+        self.tracker.remove_memo(*self._m1)
+        self.assertEqual([('removed', 'name1', 'content1')], policy.log)
+
     def test_add_with_concurrent_add(self):
         self.tracker.add_memo(*self._m1)
         self.tracker.save()
@@ -252,6 +278,26 @@
         btree = btree_index.BTreeGraphIndex(self.transport, 'meta', size=None)
         self.assertEqual(1, btree.key_count())
 
+    def test_save_calls_memo_added_removed(self):
+        self.tracker.add_memo(*self._m1)
+        self.tracker.add_memo(*self._m2)
+        self.tracker.save()
+        tracker2 = self.make_tracker()
+        tracker2.remove_memo(*self._m1)
+        tracker2.add_memo(*self._m3)
+        tracker2.save()
+        policy = LoggingUpdatePolicy()
+        self.tracker.update_policy = policy
+        self.tracker.save()
+        # When we save, tracker should notice that m1 was removed, and remove
+        # it from the in-memory structure.
+        self.assertEqual({'name2': 'content2',
+                          'name3': 'content3',
+                         }, self.tracker._memos)
+        self.assertEqual([('removed', 'name1', 'content1'),
+                          ('added', 'name3', 'content3'),
+                         ], policy.log)
+
     def test__diff_from_disk_no_changes(self):
         self.tracker.add_memo(*self._m1)
         self.tracker.save()



More information about the bazaar-commits mailing list