Skip to content

Commit

Permalink
[chunked_filter] Validate inputs, improve docstrings, add tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
Raphael Krupinski committed Jul 25, 2023
1 parent 1ca3943 commit 08dccec
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 7 deletions.
38 changes: 31 additions & 7 deletions boltons/iterutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -374,26 +374,50 @@ def chunked_iter(src, size, **kw):
return


def chunked_filter(iterable, predicate, chunk_size):
"""A version of :func:`filter` which will call predicate with a chunk of the iterable.
def chunked_filter(iterable, predicate, size):
"""A version of :func:`filter` which will call *key* with a chunk of the *src*.
>>> list(chunked_filter(range(10), lambda chunk: (x % 2==0 for x in chunk), 5))
>>> list(chunked_filter(range(10), lambda chunk: (x % 2==0 for x in chunk),5))
[0, 2, 4, 6, 8]
In the above example the lambda function is called twice: once with values
0-4 and then for 5-9.
Args:
iterable (Iterable): Items to filter
predicate (Callable): Predicate function
chunk_size (int): The maximum size of chunks that will be passed the
predicate (Callable): Bulk predicate function that accepts a list of items
and returns an interable of bools
size (int): The maximum size of chunks that will be passed the
predicate function.
The intended use case for this function is with external APIs,
for all kinds of validations. Since APIs always have limitations,
either explicitely for number of passed items, or at least for the request size,
it's required to pass large collections in chunks.
"""

if not is_iterable(iterable):
raise TypeError('expected an iterable')
size = _validate_positive_int(size, 'chunk size')

if not callable(predicate):
raise TypeError('expected callable key')

def predicate_(src_):
allow_iter = predicate(src_)
if not is_iterable(allow_iter):
raise TypeError('expected an iterable from key(src)')

allow_list = list(allow_iter)
if len(allow_list) != len(src_):
raise ValueError('expected the iterable from key(src) has the same length as the passed chunk of items')

return allow_list

return (
item
for chunk in chunked_iter(iterable, chunk_size)
for item, allow in zip(chunk, predicate(chunk))
for chunk in chunked_iter(iterable, size)
for item, allow in zip(chunk, predicate_(chunk))
if allow
)

Expand Down
20 changes: 20 additions & 0 deletions tests/test_iterutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,26 @@ def test_chunked_bytes():
assert chunked(b'123', 2) in (['12', '3'], [b'12', b'3'])


class TestChunkedFilter(object):
def test_not_iterable(self):
from boltons.iterutils import chunked_filter

with pytest.raises(TypeError):
chunked_filter(7, lambda chunk: (True for x in chunk), 10)

def test_size_zero(self):
from boltons.iterutils import chunked_filter

with pytest.raises(ValueError):
chunked_filter((1, 2, 3), lambda chunk: (True for x in chunk), 0)

def test_not_callable(self):
from boltons.iterutils import chunked_filter

with pytest.raises(TypeError):
chunked_filter((1, 2, 3), 'allow odd numbers', 10)


def test_chunk_ranges():
from boltons.iterutils import chunk_ranges

Expand Down

0 comments on commit 08dccec

Please sign in to comment.