Merge lp:~bac/lazr.batchnavigator/bug-826839 into lp:lazr.batchnavigator

Proposed by Brad Crittenden
Status: Merged
Merged at revision: 46
Proposed branch: lp:~bac/lazr.batchnavigator/bug-826839
Merge into: lp:lazr.batchnavigator
Diff against target: 172 lines (+86/-17)
3 files modified
src/lazr/batchnavigator/_batchnavigator.py (+2/-1)
src/lazr/batchnavigator/tests/test_batchnavigator.py (+83/-15)
src/lazr/batchnavigator/version.txt (+1/-1)
To merge this branch: bzr merge lp:~bac/lazr.batchnavigator/bug-826839
Reviewer Review Type Date Requested Status
Benji York (community) code Approve
Review via email: mp+72247@code.launchpad.net

Description of the change

Negative slice indexing into Storm/Postgresql empty result sets causes an exception to be raised. In that instance, prevent the index from being negative.

To post a comment you must log in.
47. By Brad Crittenden

Bump to 1.2.8

Revision history for this message
Benji York (benji) wrote :

This looks good.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/lazr/batchnavigator/_batchnavigator.py'
--- src/lazr/batchnavigator/_batchnavigator.py 2011-08-17 14:34:35 +0000
+++ src/lazr/batchnavigator/_batchnavigator.py 2011-08-19 19:06:25 +0000
@@ -363,7 +363,8 @@
363 self.results = IFiniteSequence(self.results)363 self.results = IFiniteSequence(self.results)
364 total_length = len(self.results)364 total_length = len(self.results)
365 offset = total_length365 offset = total_length
366 offset_plus_size = offset + offset_plus_size366 # Storm raises an exception for a negative initial offset.
367 offset_plus_size = max(offset + offset_plus_size, 0)
367 result = list(self.results[offset_plus_size:offset])368 result = list(self.results[offset_plus_size:offset])
368 result.reverse()369 result.reverse()
369 return result370 return result
370371
=== modified file 'src/lazr/batchnavigator/tests/test_batchnavigator.py'
--- src/lazr/batchnavigator/tests/test_batchnavigator.py 2011-08-17 14:34:35 +0000
+++ src/lazr/batchnavigator/tests/test_batchnavigator.py 2011-08-19 19:06:25 +0000
@@ -66,6 +66,35 @@
66 return BatchNavigator(collection, request, range_factory=range_factory)66 return BatchNavigator(collection, request, range_factory=range_factory)
6767
6868
69class PickyListLikeCollection:
70 """Collection that really hates slices with negative start values.
71
72 Some database-backed collections, e.g. Postgres via Storm, throw
73 exceptions if a slice is used that starts with a negative value. When
74 batch navigator is going backwards it is easy to pass such a slice. This
75 collection is used to ensure it is noticed if that happens.
76 """
77 def __init__(self, collection):
78 self._collection = collection
79 def __iter__(self):
80 return iter(self._collection)
81 def __getitem__(self, index):
82 if (isinstance(index, slice) and
83 index.start is not None and index.start < 0):
84 raise RuntimeError
85 return self._collection.__getitem__(index)
86 def __len__(self):
87 return self._collection.__len__()
88
89
90def empty_batchnav(start=None, batch=None, memo=None, direction=None):
91 collection = PickyListLikeCollection([])
92 request = batchnav_request(start=start, batch=batch, memo=memo,
93 direction=direction)
94 range_factory = RecordingFactory(collection)
95 return BatchNavigator(collection, request, range_factory=range_factory)
96
97
69class EqualsQuery(Equals):98class EqualsQuery(Equals):
7099
71 def __init__(self, start=None, batch=None, direction=None, memo=None):100 def __init__(self, start=None, batch=None, direction=None, memo=None):
@@ -80,7 +109,8 @@
80 results = [1, 2, 3, 4]109 results = [1, 2, 3, 4]
81 range_factory = ListRangeFactory(results)110 range_factory = ListRangeFactory(results)
82 request = batchnav_request()111 request = batchnav_request()
83 batchnav = BatchNavigator(results, request, range_factory=range_factory)112 batchnav = BatchNavigator(
113 results, request, range_factory=range_factory)
84 self.assertEqual(batchnav.batch.range_factory, range_factory)114 self.assertEqual(batchnav.batch.range_factory, range_factory)
85115
86 def test_without_memo_direction_gets_non_factory_batch(self):116 def test_without_memo_direction_gets_non_factory_batch(self):
@@ -167,6 +197,16 @@
167 batchnav.nextBatchURL(),197 batchnav.nextBatchURL(),
168 Equals(SERVER_URL + '?batch=3&memo=end%26%2F2&start=9'))198 Equals(SERVER_URL + '?batch=3&memo=end%26%2F2&start=9'))
169199
200 def test_empty_collection(self):
201 batchnav = empty_batchnav(
202 start=2, batch=2)
203 self.assertEqual([], list(batchnav.currentBatch()))
204
205 def test_empty_collection_backwards(self):
206 batchnav = empty_batchnav(
207 start=2, batch=2, direction='backwards')
208 self.assertEqual([], list(batchnav.currentBatch()))
209
170210
171class TestListRangeFactory(testtools.TestCase):211class TestListRangeFactory(testtools.TestCase):
172212
@@ -221,42 +261,70 @@
221 def test_getslice_next_end(self):261 def test_getslice_next_end(self):
222 # at the end, crickets...262 # at the end, crickets...
223 results = [0, 1, 2, 3, 4, 5]263 results = [0, 1, 2, 3, 4, 5]
224 slice = ListRangeFactory(results).getSlice(3, '6')264 _slice = ListRangeFactory(results).getSlice(3, '6')
225 self.assertEqual([], slice)265 self.assertEqual([], _slice)
226266
227 def test_getslice_before_start(self):267 def test_getslice_before_start(self):
228 # at the end, crickets...268 # at the end, crickets...
229 results = [0, 1, 2, 3, 4, 5]269 results = [0, 1, 2, 3, 4, 5]
230 slice = list(ListRangeFactory(results).getSlice(3, '0', forwards=False))270 _slice = list(ListRangeFactory(results).getSlice(3, '0', forwards=False))
231 self.assertEqual([], slice)271 self.assertEqual([], _slice)
232272
233 def test_getslice_before_end(self):273 def test_getslice_before_end(self):
234 results = [0, 1, 2, 3, 4, 5]274 results = [0, 1, 2, 3, 4, 5]
235 slice = list(ListRangeFactory(results).getSlice(3, '6', forwards=False))275 _slice = list(ListRangeFactory(results).getSlice(3, '6', forwards=False))
236 self.assertEqual([5, 4, 3], slice)276 self.assertEqual([5, 4, 3], _slice)
237277
238 def test_getslice_next(self):278 def test_getslice_next(self):
239 # The slice returned starts where indicated but continues on.279 # The slice returned starts where indicated but continues on.
240 results = [0, 1, 2, 3, 4, 5]280 results = [0, 1, 2, 3, 4, 5]
241 slice = ListRangeFactory(results).getSlice(3, '3')281 _slice = ListRangeFactory(results).getSlice(3, '3')
242 self.assertEqual([3, 4, 5], slice)282 self.assertEqual([3, 4, 5], _slice)
243283
244 def test_getslice_before_middle(self):284 def test_getslice_before_middle(self):
245 # Going backwards does not include the anchor (because going forwards285 # Going backwards does not include the anchor (because going forwards
246 # includes it)286 # includes it)
247 results = [0, 1, 2, 3, 4, 5]287 results = [0, 1, 2, 3, 4, 5]
248 slice = list(ListRangeFactory(results).getSlice(3, '3', forwards=False))288 _slice = list(ListRangeFactory(results).getSlice(3, '3', forwards=False))
249 self.assertEqual([2, 1, 0], slice)289 self.assertEqual([2, 1, 0], _slice)
250290
251 def test_getSliceByIndex(self):291 def test_getSliceByIndex(self):
252 # getSliceByIndex returns the slice of the result limited by292 # getSliceByIndex returns the slice of the result limited by
253 # the indices start, end.293 # the indices start, end.
254 results = [0, 1, 2, 3, 4, 5]294 results = [0, 1, 2, 3, 4, 5]
255 slice = ListRangeFactory(results).getSliceByIndex(2, 5)295 _slice = ListRangeFactory(results).getSliceByIndex(2, 5)
256 self.assertEqual([2, 3, 4], slice)296 self.assertEqual([2, 3, 4], _slice)
257297
258 def test_getSliceByIndex__no_result_set(self):298 def test_getSliceByIndex__no_result_set(self):
259 # If no result set is present, getSliceByIndex() returns an299 # If no result set is present, getSliceByIndex() returns an
260 # empty list.300 # empty list.
261 slice = ListRangeFactory(None).getSliceByIndex(2, 5)301 _slice = ListRangeFactory(None).getSliceByIndex(2, 5)
262 self.assertEqual([], slice)302 self.assertEqual([], _slice)
303
304 def test_picky_collection_ok(self):
305 p = PickyListLikeCollection(range(5))
306 self.assertEqual(range(5)[0:2], p[0:2])
307
308 def test_picky_collection_bad(self):
309 p = PickyListLikeCollection([])
310 # It would be nice to demonstrate p[-10:2] but it cannot be done
311 # directly since assertRaises needs individual parameters. The
312 # following is the equivalent.
313 self.assertRaises(RuntimeError,
314 p.__getitem__, slice(-10, 2, None))
315
316 def test_getSlice_empty_result_set_forwards(self):
317 results = PickyListLikeCollection([])
318 _slice = ListRangeFactory(results).getSlice(5, forwards=True)
319 self.assertEqual([], _slice)
320
321 def test_getSlice_empty_result_set_backwards(self):
322 results = PickyListLikeCollection([])
323 _slice = ListRangeFactory(results).getSlice(5, forwards=False)
324 self.assertEqual([], _slice)
325
326 def test_getSliceByIndex_empty_result_set(self):
327 results = PickyListLikeCollection([])
328 self.assertRaises(
329 RuntimeError,
330 ListRangeFactory(results).getSliceByIndex, -1, 1)
263331
=== modified file 'src/lazr/batchnavigator/version.txt'
--- src/lazr/batchnavigator/version.txt 2011-08-17 14:34:35 +0000
+++ src/lazr/batchnavigator/version.txt 2011-08-19 19:06:25 +0000
@@ -1,1 +1,1 @@
11.2.711.2.8

Subscribers

People subscribed via source and target branches