-
Notifications
You must be signed in to change notification settings - Fork 24
Description
What happened?
The asyncstdlib.tee()
function does not behave consistently with Python's built-in itertools.tee()
when creating multiple consumers from an already-advanced iterator. Specifically, asyncstdlib.tee()
advances all consumers to the current position of the source iterator, while itertools.tee()
correctly preserves a buffer of previously yielded values.
Environment
- asyncstdlib version: 3.13.1
- Python version: 3.12.10
- Operating System: Linux
Expected Behavior
When using itertools.tee()
with Python's built-in iterators, creating a new consumer from an already-advanced source iterator should provide access to previously yielded values through internal buffering.
Actual Behavior
When using asyncstdlib.tee()
, creating a new consumer from an already-advanced source iterator causes the new consumer to start from the current position, losing access to previously yielded values.
Root Cause Analysis
The issue appears to be that asyncstdlib.tee()
doesn't maintain the same buffering mechanism as itertools.tee()
. In the standard library implementation, when creating a new consumer from an existing tee'd iterator, the new consumer gains access to the internal buffer that contains previously yielded values.
In asyncstdlib.tee()
, it seems that new consumers are created at the current position of the source iterator without access to the historical buffer, causing them to miss previously yielded values.
Minimal Reproducible Example
# Standard library behavior (correct):
# Standard library behavior (correct):
import itertools
# Create initial source
(source,) = itertools.tee(itertools.count(), 1)
# Advance source to position 1
print(next(source)) # 0
# Create first consumer after advancing source
consumer1, = itertools.tee(source, 1)
# Advance source to position 3
print(next(source)) # 1
print(next(source)) # 2
print(next(source)) # 3
# Create second consumer after advancing even more
consumer2, = itertools.tee(source, 1)
# Advance source further
print(next(source)) # 4
print(next(source)) # 5
# Now test the consumers
print(next(consumer1)) # Outputs: 1 (starts from buffered position)
print(next(consumer2)) # Outputs: 4 (starts from buffered position)
print(next(consumer1)) # Outputs: 2 (continues from buffer)
print(next(consumer2)) # Outputs: 5 (continues from buffer)
# asyncstdlib behavior (incorrect):
import itertools
import asyncstdlib
async def infinite_counter():
for i in itertools.count():
yield i
# Create initial source
(source,) = asyncstdlib.tee(infinite_counter(), 1)
# Advance source to position 1
await anext(source) # 0
# Create first consumer after advancing source
consumer1, = asyncstdlib.tee(source, 1)
# Advance source to position 3
await anext(source) # 1
await anext(source) # 2
await anext(source) # 3
# Create second consumer after advancing even more
consumer2, = asyncstdlib.tee(source, 1)
# Advance source further
await anext(source) # 4
await anext(source) # 5
# Now test the consumers
print(await anext(consumer1)) # Outputs: 6 (should be 1!)
print(await anext(consumer2)) # Outputs: 7 (should be 4!)
print(await anext(consumer1)) # Outputs: 8 (should be 2!)
print(await anext(consumer2)) # Outputs: 9 (should be 5!)
Request Assignment [Optional]
- I already understand the cause and want to submit a bugfix.