python / cpython

The Python programming language
https://www.python.org
Other
63.61k stars 30.47k forks source link

itertools.tee lookahead/peek example doesn't work #126701

Open yggdr opened 1 week ago

yggdr commented 1 week ago

Documentation

In the tee documentation a method for making tee-iterators peekable is mentioned:

def lookahead(tee_iterator):
     "Return the next value without moving the input forward"
     [forked_iterator] = tee(tee_iterator, 1)
     return next(forked_iterator)

iterator = iter('abcdef')
[iterator] = tee(iterator, 1)   # Make the input peekable
next(iterator)                  # Move the iterator forward; prints 'a'
lookahead(iterator)             # Check next value; prints 'b'
next(iterator)                  # Continue moving forward; supposedly also prints 'b', but prints 'c' instead!

Tested on versions 3.13.0, 3.11.10, 3.9.18 (and 2.7.18 for a laugh).

123884 seems to be the source of the error, and it is indeed working correctly on 3.14@4f3253a0ccf3a512c497f779e4a6db2656c75844. But the documentation for older version should be updated, as following recipes from the official docs that don't work on the version the docs are for makes debugging a nightmare.

The docs might also want to mention a possible workaround in the form of _, iterator = tee(iterator, 2), as only the first copy from tee seems to be afflicted by the bug. At least that is my reading of its description, and it seems to work in a quick test.

rhettinger commented 1 week ago

Note, the bugfix has already been backported to 3.12 and 3.13. It looks like the online docs have publishing ahead of those bug fixes actually being released. That should resolve itself when 3.13.1 comes out. Until then, I'm thinking that it is best to temporarily remove the lookahead example entirely.

As you observed, we could incorporate the bug workaround _, iterator = tee(iterator, 2) but that has the disadvantage of being mysterious and enshrining the bug in perpetuity.

Another workaround that always worked is tee_cloner = type(tee('', 1)[0]) giving lookahead = lambda tee_iterator: next(tee_cloner(tee_iterator)). That has the disadvantage of relying on a private type that hasn't yet been exposed in the types module.

rhettinger commented 1 week ago

@erlend-aasland I removed the bug label because the bug has already been fixed. The problem here is that the doc example depends on the bug fix but the micro-release hasn't happened yet. So AFAICT this is a doc timing issue only.

erlend-aasland commented 1 week ago

FYI, we normally use the doc and bug labels in combination for incorrect documentation (aka doc bugs).