changeset 416:ef401198ebf9

storage: remove history() from storage backends api, only RouterBackend+IndexingMixIn now implements history()
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Sun, 07 Aug 2011 03:24:56 +0200
parents d755f798fe66
children d49d08bb5f50
files MoinMoin/storage/__init__.py MoinMoin/storage/_tests/test_backends.py MoinMoin/storage/_tests/test_backends_router.py MoinMoin/storage/backends/acl.py MoinMoin/storage/backends/flatfile.py MoinMoin/storage/backends/hg.py MoinMoin/storage/backends/memory.py MoinMoin/storage/backends/sqla.py
diffstat 8 files changed, 100 insertions(+), 197 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/storage/__init__.py	Sun Aug 07 03:02:46 2011 +0200
+++ b/MoinMoin/storage/__init__.py	Sun Aug 07 03:24:56 2011 +0200
@@ -165,34 +165,6 @@
         """
         raise NotImplementedError()
 
-    def history(self, reverse=True):
-        """
-        Returns an iterator over ALL revisions of ALL items stored in the backend.
-
-        If reverse is True (default), give history in reverse revision timestamp
-        order, otherwise in revision timestamp order.
-
-        Note: some functionality (e.g. completely cloning one storage into
-              another) requires that the iterator goes over really every
-              revision we have.
-
-        :type reverse: bool
-        :param reverse: Indicate whether the iterator should go in reverse order.
-        :rtype: iterator of revision objects
-        """
-        # generic and slow history implementation
-        revs = []
-        for item in self.iteritems():
-            for revno in item.list_revisions():
-                rev = item.get_revision(revno)
-                revs.append((rev.timestamp, rev.revno, item.name, ))
-        revs.sort() # from oldest to newest
-        if reverse:
-            revs.reverse()
-        for ts, revno, name in revs:
-            item = self.get_item(name)
-            yield item.get_revision(revno)
-
     def _get_revision(self, item, revno):
         """
         For a given item and revision number, return the corresponding revision
--- a/MoinMoin/storage/_tests/test_backends.py	Sun Aug 07 03:02:46 2011 +0200
+++ b/MoinMoin/storage/_tests/test_backends.py	Sun Aug 07 03:24:56 2011 +0200
@@ -590,9 +590,6 @@
         rev = item.get_revision(0)
         assert rev[SIZE] == 8
 
-        for nrev in self.backend.history():
-            assert nrev[SIZE] == 8
-
     def test_size_2(self):
         item = self.backend.create_item(u'size2')
         rev0 = item.create_revision(0)
@@ -620,57 +617,6 @@
                                        ('1', 1, 1L, 1+0j, (1, ), ), u'ąłć', (u'ó', u'żźć'), )):
             yield test_value, value, no
 
-    def test_history(self):
-        order = [(u'first', 0, ), (u'second', 0, ), (u'first', 1, ), (u'a', 0), (u'child/my_subitem', 0) ]
-        for name, revno in order:
-            if revno == 0:
-                item = self.backend.create_item(name)
-            else:
-                item = self.backend.get_item(name)
-            item.create_revision(revno)
-            item.commit()
-
-            from MoinMoin.storage.backends import router, acl
-            if isinstance(self.backend, (router.RouterBackend, acl.AclWrapperBackend)):
-                # Revisions are created too fast for the rev's timestamp's granularity.
-                # This only affects the RouterBackend because there several different
-                # backends are used and no means for storing simultaneously created revs
-                # in the correct order exists between backends. It affects AclWrapperBackend
-                # tests as well because those use a RouterBackend internally for real-world-likeness.
-
-                # XXX XXX
-                # You may have realized that all the items above belong to the same backend so this shouldn't actually matter.
-                # It does matter, however, once you consider that the RouterBackend uses the generic, slow history implementation.
-                # This one uses iteritems and then sorts all the revisions itself, hence discarding any information of ordering
-                # for simultaneously created revisions. If we just call history of that single backend directly, it works without
-                # time.sleep. For n backends, however, you'd have to somehow merge the revisions into one generator again, thus
-                # discarding that information again. Besides, that would be a costly operation. The ordering for simultaneosly
-                # created revisions remains the same since it's based on tuple ordering. Better proposals welcome.
-                import time
-                time.sleep(1)
-
-        for num, rev in enumerate(self.backend.history(reverse=False)):
-            name, revno = order[num]
-            assert rev.item.name == name
-            assert rev.revno == revno
-
-        order.reverse()
-        for num, rev in enumerate(self.backend.history()):
-            name, revno = order[num]
-            assert rev.item.name == name
-            assert rev.revno == revno
-
-    # See history function in indexing.py for comments on why this test fails.
-    @py.test.mark.xfail
-    def test_history_size_after_rename(self):
-        item = self.backend.create_item(u'first')
-        item.create_revision(0)
-        item.commit()
-        item.rename(u'second')
-        item.create_revision(1)
-        item.commit()
-        assert len([rev for rev in self.backend.history()]) == 2
-
     def test_destroy_item(self):
         itemname = u"I will be completely destroyed"
         rev_data = "I will be completely destroyed, too, hopefully"
@@ -683,14 +629,6 @@
         assert not self.backend.has_item(itemname)
         item_names = [item.name for item in self.backend.iteritems()]
         assert not itemname in item_names
-        all_rev_data = [rev.read() for rev in self.backend.history()]
-        assert not rev_data in all_rev_data
-
-        for rev in self.backend.history():
-            assert not rev.item.name == itemname
-        for rev in self.backend.history(reverse=False):
-            assert not rev.item.name == itemname
-
 
     def test_destroy_revision(self):
         itemname = u"I will see my children die :-("
@@ -726,9 +664,6 @@
         assert last_data != third
         assert last_data == persistent_rev
 
-        for rev in self.backend.history():
-            assert not (rev.item.name == itemname and rev.revno == 2)
-
     def test_clone_backend(self):
         src = flaskg.storage
         dst = memory.MemoryBackend()
@@ -749,7 +684,6 @@
         dst.clone(src, verbose=False)
 
         assert len(list(dst.iteritems())) == 2
-        assert len(list(dst.history())) == 1
         assert dst.has_item(dollys_name)
         rev = dst.get_item(dollys_name).get_revision(0)
         data = rev.read()
@@ -793,17 +727,3 @@
         item.destroy()
         assert len([item for item in self.backend.iteritems()]) == 0
 
-    def test_history_item_names(self):
-        item = self.backend.create_item(u'first')
-        item.create_revision(0)
-        item.commit()
-        item.rename(u'second')
-        item.create_revision(1)
-        item.commit()
-        revs_in_create_order = [rev for rev in self.backend.history(reverse=False)]
-        assert revs_in_create_order[0].revno == 0
-        assert revs_in_create_order[0].item.name == u'second'
-        assert revs_in_create_order[1].revno == 1
-        assert revs_in_create_order[1].item.name == u'second'
-
-
--- a/MoinMoin/storage/_tests/test_backends_router.py	Sun Aug 07 03:02:46 2011 +0200
+++ b/MoinMoin/storage/_tests/test_backends_router.py	Sun Aug 07 03:24:56 2011 +0200
@@ -129,3 +129,103 @@
         assert backend is self.child
         assert name == ''
         assert mountpoint == 'child'
+
+
+    def test_history(self):
+        order = [(u'first', 0, ), (u'second', 0, ), (u'first', 1, ), (u'a', 0), (u'child/my_subitem', 0) ]
+        for name, revno in order:
+            if revno == 0:
+                item = self.backend.create_item(name)
+            else:
+                item = self.backend.get_item(name)
+            item.create_revision(revno)
+            item.commit()
+
+            # Revisions are created too fast for the rev's timestamp's granularity.
+            # This only affects the RouterBackend because there several different
+            # backends are used and no means for storing simultaneously created revs
+            # in the correct order exists between backends. It affects AclWrapperBackend
+            # tests as well because those use a RouterBackend internally for real-world-likeness.
+
+            # XXX XXX
+            # You may have realized that all the items above belong to the same backend so this shouldn't actually matter.
+            # It does matter, however, once you consider that the RouterBackend uses the generic, slow history implementation.
+            # This one uses iteritems and then sorts all the revisions itself, hence discarding any information of ordering
+            # for simultaneously created revisions. If we just call history of that single backend directly, it works without
+            # time.sleep. For n backends, however, you'd have to somehow merge the revisions into one generator again, thus
+            # discarding that information again. Besides, that would be a costly operation. The ordering for simultaneosly
+            # created revisions remains the same since it's based on tuple ordering. Better proposals welcome.
+            import time
+            time.sleep(1)
+
+        for num, rev in enumerate(self.backend.history(reverse=False)):
+            name, revno = order[num]
+            assert rev.item.name == name
+            assert rev.revno == revno
+
+        order.reverse()
+        for num, rev in enumerate(self.backend.history(reverse=True)):
+            name, revno = order[num]
+            assert rev.item.name == name
+            assert rev.revno == revno
+
+    # See history function in indexing.py for comments on why this test fails.
+    @py.test.mark.xfail
+    def test_history_size_after_rename(self):
+        item = self.backend.create_item(u'first')
+        item.create_revision(0)
+        item.commit()
+        item.rename(u'second')
+        item.create_revision(1)
+        item.commit()
+        assert len([rev for rev in self.backend.history()]) == 2
+
+    def test_history_after_destroy_item(self):
+        itemname = u"I will be completely destroyed"
+        rev_data = "I will be completely destroyed, too, hopefully"
+        item = self.backend.create_item(itemname)
+        rev = item.create_revision(0)
+        rev.write(rev_data)
+        item.commit()
+
+        item.destroy()
+
+        all_rev_data = [rev.read() for rev in self.backend.history()]
+        assert not rev_data in all_rev_data
+
+        for rev in self.backend.history():
+            assert not rev.item.name == itemname
+        for rev in self.backend.history(reverse=False):
+            assert not rev.item.name == itemname
+
+    def test_history_after_destroy_revision(self):
+        itemname = u"I will see my children die :-("
+        rev_data = "I will die!"
+        persistent_rev = "I will see my sibling die :-("
+        item = self.backend.create_item(itemname)
+        rev = item.create_revision(0)
+        rev.write(rev_data)
+        item.commit()
+        rev = item.create_revision(1)
+        rev.write(persistent_rev)
+        item.commit()
+
+        rev = item.get_revision(0)
+        rev.destroy()
+
+        for rev in self.backend.history():
+            assert not (rev.item.name == itemname and rev.revno == 0)
+
+    def test_history_item_names(self):
+        item = self.backend.create_item(u'first')
+        item.create_revision(0)
+        item.commit()
+        item.rename(u'second')
+        item.create_revision(1)
+        item.commit()
+        revs_in_create_order = [rev for rev in self.backend.history(reverse=False)]
+        assert revs_in_create_order[0].revno == 0
+        assert revs_in_create_order[0].item.name == u'second'
+        assert revs_in_create_order[1].revno == 1
+        assert revs_in_create_order[1].item.name == u'second'
+
--- a/MoinMoin/storage/backends/acl.py	Sun Aug 07 03:02:46 2011 +0200
+++ b/MoinMoin/storage/backends/acl.py	Sun Aug 07 03:24:56 2011 +0200
@@ -146,20 +146,6 @@
 
     iteritems = iter_items_noindex
 
-    def history(self, reverse=True):
-        """
-        @see: Backend.history.__doc__
-        """
-        for revision in self.backend.history(reverse):
-            if self._may(revision.item.name, READ):
-                # The revisions returned here should only be StoredRevisions.
-                # We wrap them nevertheless to be sure. Esp. revision.item
-                # would otherwise give access to an unwrapped item.
-                item = revision.item
-                item = AclWrapperItem(item, self)
-                revision = AclWrapperRevision(revision, item)
-                yield revision
-
     def _get_acl(self, itemname):
         """
         Get ACL strings from the last revision's metadata and return ACL object.
--- a/MoinMoin/storage/backends/flatfile.py	Sun Aug 07 03:02:46 2011 +0200
+++ b/MoinMoin/storage/backends/flatfile.py	Sun Aug 07 03:24:56 2011 +0200
@@ -63,13 +63,6 @@
         revpath = self._rev_path(name)
         return os.path.exists(revpath)
 
-    def history(self, reverse=True):
-        rev_list = [i.get_revision(-1) for i in self.iteritems()]
-        rev_list.sort(lambda x, y: cmp(x.timestamp, y.timestamp))
-        if reverse:
-            rev_list.reverse()
-        return iter(rev_list)
-
     def get_item(self, itemname):
         if not self._exists(itemname):
             raise NoSuchItemError("No such item, %r" % (itemname))
--- a/MoinMoin/storage/backends/hg.py	Sun Aug 07 03:02:46 2011 +0200
+++ b/MoinMoin/storage/backends/hg.py	Sun Aug 07 03:24:56 2011 +0200
@@ -156,39 +156,6 @@
 
     iteritems = iter_items_noindex
 
-    def history(self, reverse=True):
-        """
-        Return generator for iterating in given direction over Item Revisions
-        with timestamp order preserved.
-        Yields MercurialStoredRevision objects.
-        """
-        def restore_revision(name, id):
-            item = Item(self, name)
-            item._id = id
-            rev = MercurialStoredRevision(item, revno)
-            rev._item_id = item._id
-            return rev
-
-        # this is costly operation, but no better idea now how to do it and not
-        # break pull/merge stuff
-        renamed_items = {}
-        for ctx in self._iter_changelog(filter_meta='renamed_to'):
-            meta = self._decode_metadata(ctx.extra(), BACKEND_METADATA_PREFIX)
-            oldid, renamed_to = meta['renamed_id'], meta['renamed_to']
-            renamed_items.setdefault(oldid, []).append(renamed_to)
-
-        for ctx in self._iter_changelog(reverse=reverse):
-            meta = self._decode_metadata(ctx.extra(), BACKEND_METADATA_PREFIX)
-            revno, oldid, oldname = meta['rev'], meta['id'], meta['name']
-            try:
-                for (id, name) in renamed_items[oldid]:
-                    # consider you have backend merged from two instances,
-                    # where there was item A renamed to B in first, and the same A
-                    # renamed to C in second
-                    yield restore_revision(name, id)
-            except KeyError:
-                yield restore_revision(oldname, oldid)
-
     def _get_revision(self, item, revno):
         """
         Return given Revision of an Item. Raise NoSuchRevisionError
--- a/MoinMoin/storage/backends/memory.py	Sun Aug 07 03:02:46 2011 +0200
+++ b/MoinMoin/storage/backends/memory.py	Sun Aug 07 03:24:56 2011 +0200
@@ -66,16 +66,6 @@
         self._item_metadata = {}            # {id : {metadata}}
         self._item_revisions = {}           # {id : {revision_id : (revision_data, {revision_metadata})}}
         self._item_metadata_lock = {}       # {id : Lockobject}
-        self._revision_history = []
-
-    def history(self, reverse=True):
-        """
-        @see: Backend.history.__doc__
-        """
-        if reverse:
-            return iter(self._revision_history[::-1])
-        else:
-            return iter(self._revision_history)
 
     def get_item(self, itemname):
         """
@@ -139,11 +129,6 @@
             except KeyError:
                 pass
 
-        # Create a new revision_history list first and then swap that atomically with
-        # the old one (that still contains the item's revs).
-        rev_hist = [rev for rev in self._revision_history if rev.item.name != item.name]
-        self._revision_history = rev_hist
-
     def iter_items_noindex(self):
         """
         @see: Backend.iter_items_noindex.__doc__
@@ -216,10 +201,6 @@
             # The revision has already been destroyed by someone else. No need to make our hands dirty.
             return
 
-        # Remove the rev from history
-        rev_history = [rev for rev in self._revision_history if (rev.item.name != revision.item.name or rev.revno != revision.revno)]
-        self._revision_history = rev_history
-
     def _rename_item(self, item, newname):
         """
         @see: Backend._rename_item.__doc__
@@ -273,8 +254,6 @@
         if revision._metadata is None:
             revision._metadata = {}
         self._item_revisions[item._item_id][revision.revno] = (revision._data.getvalue(), revision._metadata.copy())
-        revision = item.get_revision(revision.revno)
-        self._revision_history.append(revision)
 
     def _rollback_item(self, rev):
         """
--- a/MoinMoin/storage/backends/sqla.py	Sun Aug 07 03:02:46 2011 +0200
+++ b/MoinMoin/storage/backends/sqla.py	Sun Aug 07 03:24:56 2011 +0200
@@ -177,20 +177,6 @@
         item = SQLAItem(self, itemname)
         return item
 
-    def history(self, reverse=True):
-        """
-        @see: Backend.history.__doc__
-        """
-        session = self.Session()
-        col = SQLARevision.id
-        if reverse:
-            col = col.desc()
-        for rev in session.query(SQLARevision).order_by(col).yield_per(1):
-            # yield_per(1) says: Don't load them into memory all at once.
-            rev.setup(self)
-            yield rev
-        session.close()
-
     def iter_items_noindex(self):
         """
         Returns an iterator over all items available in this backend.