Here are two solutions using tee.
The troubled original for comparison:
while True: chunk = takewhile(getNewChunkLogic(), myIterator) process(chunk)
My first:
while True: myIterator, copy = tee(myIterator) chunk = ( next(myIterator) for _ in takewhile(getNewChunkLogic(), copy) ) process(chunk)
Before each chunk, we create a copy of the iterator. Then we run takewhile on the copy, so that it "eats" the extra element only from the copy. We ignore the values we get from takewhile, we only use them to take as many elements from the main iterator.
My second way is similar:
while True: myIterator, copy = tee(myIterator) chunk = compress( takewhile(getNewChunkLogic(), copy), zip(myIterator) ) process(chunk)
Again we use takewhile on a copy, but this time we use its outputs as the chunk. And we use compress to pull the main iterator along so it ends up at the correct position for the next chunk. Using zip is a little trick, it wraps the elements in 1-tuples, which are all true, so compress doesn't throw anything away.
Full demo (Attempt This Online!):
from itertools import * import sys myIterator = count(1) getNewChunkLogic = iter([ lambda x: x <= 10, lambda x: x <= 20, lambda x: False, lambda x: x <= 30, ]).__next__ def process(chunk): for x in chunk: print(x, end=' ') if x == 30: sys.exit() print() while True: chunk = takewhile(getNewChunkLogic(), myIterator) process(chunk)
With your question's code, takewhile for the up-to-10 chunk eats the 11, the up-to-20 chunk eats the 21, and the empty chunk eats the 22:
1 2 3 4 5 6 7 8 9 10 12 13 14 15 16 17 18 19 20 23 24 25 26 27 28 29 30
With my solutions, we get the desired results (try first and second):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30