Skip to content

Commit ba09f1c

Browse files
committed
Honour caller's 'limit' in iterview.
1 parent 86fd3b5 commit ba09f1c

File tree

2 files changed

+32
-7
lines changed

2 files changed

+32
-7
lines changed

couchdb/client.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
>>> del server['python-tests']
2424
"""
2525

26+
import itertools
2627
import mimetypes
2728
import os
2829
from types import FunctionType
@@ -844,19 +845,30 @@ def iterview(self, name, batch, wrapper=None, **options):
844845
:param options: optional query string parameters
845846
:return: row generator
846847
"""
848+
# Check sane batch size.
847849
if batch <= 0:
848850
raise ValueError('batch must be 1 or more')
849-
options['limit'] = batch + 1 # XXX todo: don't overwrite caller's limit
851+
# Save caller's limit, it must be handled manually.
852+
limit = options.get('limit')
853+
if limit is not None and limit <= 0:
854+
raise ValueError('limit must be 1 or more')
850855
startkey, startkey_docid = None, None # XXX todo: honour caller's startkey
851856
while True:
852-
options.update(startkey=startkey, startkey_docid=startkey_docid)
853-
# Get a batch of rows, with one extra for start of next batch.
857+
loop_limit = min(limit or batch, batch)
858+
# Get rows in batches, with one extra for start of next batch.
859+
options.update(limit=loop_limit + 1, startkey=startkey,
860+
startkey_docid=startkey_docid)
854861
rows = list(self.view(name, wrapper, **options))
855-
# Yield at most 'batch' rows.
856-
for row in rows[:batch]:
862+
# Yield rows from this batch.
863+
for row in itertools.islice(rows, loop_limit):
857864
yield row
858-
if len(rows) <= batch:
865+
# Decrement limit counter.
866+
if limit is not None:
867+
limit -= min(len(rows), batch)
868+
# Check if there is nothing else to yield.
869+
if len(rows) <= batch or (limit is not None and limit == 0):
859870
break
871+
# Save start keys for next loop.
860872
startkey, startkey_docid = rows[-1]['key'], rows[-1]['id']
861873

862874
def show(self, name, docid=None, **options):

couchdb/tests/client.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -730,7 +730,7 @@ def test_update_doc(self):
730730

731731
class ViewIterationTestCase(testutil.TempDatabaseMixin, unittest.TestCase):
732732

733-
num_docs = 10
733+
num_docs = 100
734734

735735
def docfromnum(self, num):
736736
return {'_id': unicode(num), 'num': int(num / 2)}
@@ -763,6 +763,19 @@ def test_batchsizes(self):
763763
self.assertEqual(len(list(self.db.iterview('test/nums', self.num_docs))), self.num_docs)
764764
self.assertEqual(len(list(self.db.iterview('test/nums', self.num_docs + 1))), self.num_docs)
765765

766+
def test_limit(self):
767+
# limit=0 doesn't make sense for iterview.
768+
self.assertRaises(ValueError, self.db.iterview('test/nums', 10, limit=0).next)
769+
# Test various limit sizes that are likely to cause trouble.
770+
for limit in [1, int(self.num_docs / 4), self.num_docs - 1, self.num_docs,
771+
self.num_docs + 1]:
772+
self.assertEqual([self.docfromrow(doc) for doc in self.db.iterview('test/nums', 10, limit=limit)],
773+
[self.docfromnum(x) for x in xrange(min(limit, self.num_docs))])
774+
# Test limit same as batch size, in case of weird edge cases.
775+
limit = int(self.num_docs / 4)
776+
self.assertEqual([self.docfromrow(doc) for doc in self.db.iterview('test/nums', limit, limit=limit)],
777+
[self.docfromnum(x) for x in xrange(limit)])
778+
766779

767780
def suite():
768781
suite = unittest.TestSuite()

0 commit comments

Comments
 (0)