浏览代码

hashindex: fix iterator implementation

NSKeyIterator and ChunkKeyIterator raised StopIteration once only when they reached their end.

But they did not raise StopIteration if one called next() again after they were exhausted,
so they did not comply to the standard iterator protocol.

AFAIK, this did not cause actual problems due to the way these iterators are used,
but when I tried to use itertools.islice() to get n-long sequences from these iterators,
it failed / went into an endless loop.
Thomas Waldmann 9 年之前
父节点
当前提交
8fd0e07a1c
共有 2 个文件被更改,包括 14 次插入1 次删除
  1. 10 0
      borg/hashindex.pyx
  2. 4 1
      borg/testsuite/hashindex.py

+ 10 - 0
borg/hashindex.pyx

@@ -168,17 +168,22 @@ cdef class NSKeyIterator:
     cdef HashIndex *index
     cdef const void *key
     cdef int key_size
+    cdef int exhausted
 
     def __cinit__(self, key_size):
         self.key = NULL
         self.key_size = key_size
+        self.exhausted = 0
 
     def __iter__(self):
         return self
 
     def __next__(self):
+        if self.exhausted:
+            raise StopIteration
         self.key = hashindex_next_key(self.index, <char *>self.key)
         if not self.key:
+            self.exhausted = 1
             raise StopIteration
         cdef uint32_t *value = <uint32_t *>(self.key + self.key_size)
         cdef uint32_t segment = _le32toh(value[0])
@@ -330,17 +335,22 @@ cdef class ChunkKeyIterator:
     cdef HashIndex *index
     cdef const void *key
     cdef int key_size
+    cdef int exhausted
 
     def __cinit__(self, key_size):
         self.key = NULL
         self.key_size = key_size
+        self.exhausted = 0
 
     def __iter__(self):
         return self
 
     def __next__(self):
+        if self.exhausted:
+            raise StopIteration
         self.key = hashindex_next_key(self.index, <char *>self.key)
         if not self.key:
+            self.exhausted = 1
             raise StopIteration
         cdef uint32_t *value = <uint32_t *>(self.key + self.key_size)
         cdef uint32_t refcount = _le32toh(value[0])

+ 4 - 1
borg/testsuite/hashindex.py

@@ -83,8 +83,11 @@ class HashIndexTestCase(BaseTestCase):
         idx = NSIndex()
         for x in range(100):
             idx[H(x)] = x, x
-        all = list(idx.iteritems())
+        iterator = idx.iteritems()
+        all = list(iterator)
         self.assert_equal(len(all), 100)
+        # iterator is already exhausted by list():
+        self.assert_raises(StopIteration, next, iterator)
         second_half = list(idx.iteritems(marker=all[49][0]))
         self.assert_equal(len(second_half), 50)
         self.assert_equal(second_half, all[50:])