From a12abd3a5ec59c2cf9d4ebf0354ae4bb8699c5a6 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Fri, 4 Sep 2026 04:48:04 +0200 Subject: [PATCH] fix: _cut_iter yields wrong-length parts when step > n partition()/chunks() on an iterator (_cut_iter) only correctly skipped step items between parts when step <= n. For step > n, slicing an n-length pool by pool[step:] silently clips to an empty list, then islice(it, step) pulled step (not n) fresh items into the pool instead of first discarding the step - n items between parts. This produced wrong-length, wrong-content chunks that diverged from the Sequence code path (_cut_seq) for the same input, e.g. list(range(10)) vs iter(range(10)) with n=2, step=3. Branch on step < n to keep the existing sliding-window logic, and for step >= n explicitly discard the gap before taking the next pool. --- funcy/seqs.py | 8 ++++++-- tests/test_seqs.py | 12 ++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/funcy/seqs.py b/funcy/seqs.py index 760a18e..d2e80fa 100644 --- a/funcy/seqs.py +++ b/funcy/seqs.py @@ -363,8 +363,12 @@ def _cut_iter(drop_tail, n, step, seq): if len(pool) < n: break yield pool - pool = pool[step:] - pool.extend(islice(it, step)) + if step < n: + pool = pool[step:] + pool.extend(islice(it, step)) + else: + take(step - n, it) # discard the gap between parts + pool = take(n, it) if not drop_tail: for item in _cut_seq(drop_tail, n, step, pool): yield item diff --git a/tests/test_seqs.py b/tests/test_seqs.py index 4a4c0e6..e3077ef 100644 --- a/tests/test_seqs.py +++ b/tests/test_seqs.py @@ -170,6 +170,18 @@ def test_chunks(): assert lchunks(2, 1, [0, 1, 2, 3]) == [[0, 1], [1, 2], [2, 3], [3]] assert lchunks(3, 1, iter(range(3))) == [[0, 1, 2], [1, 2], [2]] +def test_partition_step_gt_n(): + # Iterator code path must skip step - n items between parts, + # matching the Sequence code path, when step > n. + assert lpartition(2, 3, iter(range(10))) == lpartition(2, 3, list(range(10))) + assert lpartition(2, 3, iter(range(10))) == [[0, 1], [3, 4], [6, 7]] + +def test_chunks_step_gt_n(): + # Iterator code path must skip step - n items between parts, + # matching the Sequence code path, when step > n. + assert lchunks(2, 3, iter(range(10))) == lchunks(2, 3, list(range(10))) + assert lchunks(2, 3, iter(range(10))) == [[0, 1], [3, 4], [6, 7], [9]] + def test_partition_by(): assert lpartition_by(lambda x: x == 3, [1,2,3,4,5]) == [[1,2], [3], [4,5]] assert lpartition_by('x', 'abxcd') == [['a', 'b'], ['x'], ['c', 'd']]