@@ -1612,10 +1612,11 @@ def test_tee(self):
16121612 self .assertEqual (len (result ), n )
16131613 self .assertEqual ([list (x ) for x in result ], [list ('abc' )]* n )
16141614
1615- # tee pass-through to copyable iterator
1615+ # tee objects are independent (see bug gh-123884)
16161616 a , b = tee ('abc' )
16171617 c , d = tee (a )
1618- self .assertTrue (a is c )
1618+ e , f = tee (c )
1619+ self .assertTrue (len ({a , b , c , d , e , f }) == 6 )
16191620
16201621 # test tee_new
16211622 t1 , t2 = tee ('abc' )
@@ -2029,6 +2030,172 @@ def test_islice_recipe(self):
20292030 self .assertEqual (next (c ), 3 )
20302031
20312032
2033+ def test_tee_recipe (self ):
2034+
2035+ # Begin tee() recipe ###########################################
2036+
2037+ def tee (iterable , n = 2 ):
2038+ if n < 0 :
2039+ raise ValueError
2040+ if n == 0 :
2041+ return ()
2042+ iterator = _tee (iterable )
2043+ result = [iterator ]
2044+ for _ in range (n - 1 ):
2045+ result .append (_tee (iterator ))
2046+ return tuple (result )
2047+
2048+ class _tee :
2049+
2050+ def __init__ (self , iterable ):
2051+ it = iter (iterable )
2052+ if isinstance (it , _tee ):
2053+ self .iterator = it .iterator
2054+ self .link = it .link
2055+ else :
2056+ self .iterator = it
2057+ self .link = [None , None ]
2058+
2059+ def __iter__ (self ):
2060+ return self
2061+
2062+ def __next__ (self ):
2063+ link = self .link
2064+ if link [1 ] is None :
2065+ link [0 ] = next (self .iterator )
2066+ link [1 ] = [None , None ]
2067+ value , self .link = link
2068+ return value
2069+
2070+ # End tee() recipe #############################################
2071+
2072+ n = 200
2073+
2074+ a , b = tee ([]) # test empty iterator
2075+ self .assertEqual (list (a ), [])
2076+ self .assertEqual (list (b ), [])
2077+
2078+ a , b = tee (irange (n )) # test 100% interleaved
2079+ self .assertEqual (lzip (a ,b ), lzip (range (n ), range (n )))
2080+
2081+ a , b = tee (irange (n )) # test 0% interleaved
2082+ self .assertEqual (list (a ), list (range (n )))
2083+ self .assertEqual (list (b ), list (range (n )))
2084+
2085+ a , b = tee (irange (n )) # test dealloc of leading iterator
2086+ for i in range (100 ):
2087+ self .assertEqual (next (a ), i )
2088+ del a
2089+ self .assertEqual (list (b ), list (range (n )))
2090+
2091+ a , b = tee (irange (n )) # test dealloc of trailing iterator
2092+ for i in range (100 ):
2093+ self .assertEqual (next (a ), i )
2094+ del b
2095+ self .assertEqual (list (a ), list (range (100 , n )))
2096+
2097+ for j in range (5 ): # test randomly interleaved
2098+ order = [0 ]* n + [1 ]* n
2099+ random .shuffle (order )
2100+ lists = ([], [])
2101+ its = tee (irange (n ))
2102+ for i in order :
2103+ value = next (its [i ])
2104+ lists [i ].append (value )
2105+ self .assertEqual (lists [0 ], list (range (n )))
2106+ self .assertEqual (lists [1 ], list (range (n )))
2107+
2108+ # test argument format checking
2109+ self .assertRaises (TypeError , tee )
2110+ self .assertRaises (TypeError , tee , 3 )
2111+ self .assertRaises (TypeError , tee , [1 ,2 ], 'x' )
2112+ self .assertRaises (TypeError , tee , [1 ,2 ], 3 , 'x' )
2113+
2114+ # tee object should be instantiable
2115+ a , b = tee ('abc' )
2116+ c = type (a )('def' )
2117+ self .assertEqual (list (c ), list ('def' ))
2118+
2119+ # test long-lagged and multi-way split
2120+ a , b , c = tee (range (2000 ), 3 )
2121+ for i in range (100 ):
2122+ self .assertEqual (next (a ), i )
2123+ self .assertEqual (list (b ), list (range (2000 )))
2124+ self .assertEqual ([next (c ), next (c )], list (range (2 )))
2125+ self .assertEqual (list (a ), list (range (100 ,2000 )))
2126+ self .assertEqual (list (c ), list (range (2 ,2000 )))
2127+
2128+ # test invalid values of n
2129+ self .assertRaises (TypeError , tee , 'abc' , 'invalid' )
2130+ self .assertRaises (ValueError , tee , [], - 1 )
2131+
2132+ for n in range (5 ):
2133+ result = tee ('abc' , n )
2134+ self .assertEqual (type (result ), tuple )
2135+ self .assertEqual (len (result ), n )
2136+ self .assertEqual ([list (x ) for x in result ], [list ('abc' )]* n )
2137+
2138+ # tee objects are independent (see bug gh-123884)
2139+ a , b = tee ('abc' )
2140+ c , d = tee (a )
2141+ e , f = tee (c )
2142+ self .assertTrue (len ({a , b , c , d , e , f }) == 6 )
2143+
2144+ # test tee_new
2145+ t1 , t2 = tee ('abc' )
2146+ tnew = type (t1 )
2147+ self .assertRaises (TypeError , tnew )
2148+ self .assertRaises (TypeError , tnew , 10 )
2149+ t3 = tnew (t1 )
2150+ self .assertTrue (list (t1 ) == list (t2 ) == list (t3 ) == list ('abc' ))
2151+
2152+ # test that tee objects are weak referencable
2153+ a , b = tee (range (10 ))
2154+ p = weakref .proxy (a )
2155+ self .assertEqual (getattr (p , '__class__' ), type (b ))
2156+ del a
2157+ gc .collect () # For PyPy or other GCs.
2158+ self .assertRaises (ReferenceError , getattr , p , '__class__' )
2159+
2160+ ans = list ('abc' )
2161+ long_ans = list (range (10000 ))
2162+
2163+ # Tests not applicable to the tee() recipe
2164+ if False :
2165+ # check copy
2166+ a , b = tee ('abc' )
2167+ self .assertEqual (list (copy .copy (a )), ans )
2168+ self .assertEqual (list (copy .copy (b )), ans )
2169+ a , b = tee (list (range (10000 )))
2170+ self .assertEqual (list (copy .copy (a )), long_ans )
2171+ self .assertEqual (list (copy .copy (b )), long_ans )
2172+
2173+ # check partially consumed copy
2174+ a , b = tee ('abc' )
2175+ take (2 , a )
2176+ take (1 , b )
2177+ self .assertEqual (list (copy .copy (a )), ans [2 :])
2178+ self .assertEqual (list (copy .copy (b )), ans [1 :])
2179+ self .assertEqual (list (a ), ans [2 :])
2180+ self .assertEqual (list (b ), ans [1 :])
2181+ a , b = tee (range (10000 ))
2182+ take (100 , a )
2183+ take (60 , b )
2184+ self .assertEqual (list (copy .copy (a )), long_ans [100 :])
2185+ self .assertEqual (list (copy .copy (b )), long_ans [60 :])
2186+ self .assertEqual (list (a ), long_ans [100 :])
2187+ self .assertEqual (list (b ), long_ans [60 :])
2188+
2189+ # Issue 13454: Crash when deleting backward iterator from tee()
2190+ forward , backward = tee (repeat (None , 2000 )) # 20000000
2191+ try :
2192+ any (forward ) # exhaust the iterator
2193+ del backward
2194+ except :
2195+ del forward , backward
2196+ raise
2197+
2198+
20322199class TestGC (unittest .TestCase ):
20332200
20342201 def makecycle (self , iterator , container ):
0 commit comments