@@ -1288,12 +1288,16 @@ def product1(*args, **kwds):
12881288 else :
12891289 return
12901290
1291- def product2 (* args , ** kwds ):
1291+ def product2 (* iterables , repeat = 1 ):
12921292 'Pure python version used in docs'
1293- pools = list (map (tuple , args )) * kwds .get ('repeat' , 1 )
1293+ if repeat < 0 :
1294+ raise ValueError ('repeat argument cannot be negative' )
1295+ pools = [tuple (pool ) for pool in iterables ] * repeat
1296+
12941297 result = [[]]
12951298 for pool in pools :
12961299 result = [x + [y ] for x in result for y in pool ]
1300+
12971301 for prod in result :
12981302 yield tuple (prod )
12991303
@@ -2062,6 +2066,161 @@ def test_islice_recipe(self):
20622066 self .assertEqual (next (c ), 3 )
20632067
20642068
2069+ def test_tee_recipe (self ):
2070+
2071+ # Begin tee() recipe ###########################################
2072+
2073+ def tee (iterable , n = 2 ):
2074+ if n < 0 :
2075+ raise ValueError ('n must be >= 0' )
2076+ iterator = iter (iterable )
2077+ shared_link = [None , None ]
2078+ return tuple (_tee (iterator , shared_link ) for _ in range (n ))
2079+
2080+ def _tee (iterator , link ):
2081+ try :
2082+ while True :
2083+ if link [1 ] is None :
2084+ link [0 ] = next (iterator )
2085+ link [1 ] = [None , None ]
2086+ value , link = link
2087+ yield value
2088+ except StopIteration :
2089+ return
2090+
2091+ # End tee() recipe #############################################
2092+
2093+ n = 200
2094+
2095+ a , b = tee ([]) # test empty iterator
2096+ self .assertEqual (list (a ), [])
2097+ self .assertEqual (list (b ), [])
2098+
2099+ a , b = tee (irange (n )) # test 100% interleaved
2100+ self .assertEqual (lzip (a ,b ), lzip (range (n ), range (n )))
2101+
2102+ a , b = tee (irange (n )) # test 0% interleaved
2103+ self .assertEqual (list (a ), list (range (n )))
2104+ self .assertEqual (list (b ), list (range (n )))
2105+
2106+ a , b = tee (irange (n )) # test dealloc of leading iterator
2107+ for i in range (100 ):
2108+ self .assertEqual (next (a ), i )
2109+ del a
2110+ self .assertEqual (list (b ), list (range (n )))
2111+
2112+ a , b = tee (irange (n )) # test dealloc of trailing iterator
2113+ for i in range (100 ):
2114+ self .assertEqual (next (a ), i )
2115+ del b
2116+ self .assertEqual (list (a ), list (range (100 , n )))
2117+
2118+ for j in range (5 ): # test randomly interleaved
2119+ order = [0 ]* n + [1 ]* n
2120+ random .shuffle (order )
2121+ lists = ([], [])
2122+ its = tee (irange (n ))
2123+ for i in order :
2124+ value = next (its [i ])
2125+ lists [i ].append (value )
2126+ self .assertEqual (lists [0 ], list (range (n )))
2127+ self .assertEqual (lists [1 ], list (range (n )))
2128+
2129+ # test argument format checking
2130+ self .assertRaises (TypeError , tee )
2131+ self .assertRaises (TypeError , tee , 3 )
2132+ self .assertRaises (TypeError , tee , [1 ,2 ], 'x' )
2133+ self .assertRaises (TypeError , tee , [1 ,2 ], 3 , 'x' )
2134+
2135+ # Tests not applicable to the tee() recipe
2136+ if False :
2137+ # tee object should be instantiable
2138+ a , b = tee ('abc' )
2139+ c = type (a )('def' )
2140+ self .assertEqual (list (c ), list ('def' ))
2141+
2142+ # test long-lagged and multi-way split
2143+ a , b , c = tee (range (2000 ), 3 )
2144+ for i in range (100 ):
2145+ self .assertEqual (next (a ), i )
2146+ self .assertEqual (list (b ), list (range (2000 )))
2147+ self .assertEqual ([next (c ), next (c )], list (range (2 )))
2148+ self .assertEqual (list (a ), list (range (100 ,2000 )))
2149+ self .assertEqual (list (c ), list (range (2 ,2000 )))
2150+
2151+ # test invalid values of n
2152+ self .assertRaises (TypeError , tee , 'abc' , 'invalid' )
2153+ self .assertRaises (ValueError , tee , [], - 1 )
2154+
2155+ for n in range (5 ):
2156+ result = tee ('abc' , n )
2157+ self .assertEqual (type (result ), tuple )
2158+ self .assertEqual (len (result ), n )
2159+ self .assertEqual ([list (x ) for x in result ], [list ('abc' )]* n )
2160+
2161+
2162+ # Tests not applicable to the tee() recipe
2163+ if False :
2164+ # tee pass-through to copyable iterator
2165+ a , b = tee ('abc' )
2166+ c , d = tee (a )
2167+ self .assertTrue (a is c )
2168+
2169+ # test tee_new
2170+ t1 , t2 = tee ('abc' )
2171+ tnew = type (t1 )
2172+ self .assertRaises (TypeError , tnew )
2173+ self .assertRaises (TypeError , tnew , 10 )
2174+ t3 = tnew (t1 )
2175+ self .assertTrue (list (t1 ) == list (t2 ) == list (t3 ) == list ('abc' ))
2176+
2177+ # test that tee objects are weak referencable
2178+ a , b = tee (range (10 ))
2179+ p = weakref .proxy (a )
2180+ self .assertEqual (getattr (p , '__class__' ), type (b ))
2181+ del a
2182+ gc .collect () # For PyPy or other GCs.
2183+ self .assertRaises (ReferenceError , getattr , p , '__class__' )
2184+
2185+ ans = list ('abc' )
2186+ long_ans = list (range (10000 ))
2187+
2188+ # Tests not applicable to the tee() recipe
2189+ if False :
2190+ # check copy
2191+ a , b = tee ('abc' )
2192+ self .assertEqual (list (copy .copy (a )), ans )
2193+ self .assertEqual (list (copy .copy (b )), ans )
2194+ a , b = tee (list (range (10000 )))
2195+ self .assertEqual (list (copy .copy (a )), long_ans )
2196+ self .assertEqual (list (copy .copy (b )), long_ans )
2197+
2198+ # check partially consumed copy
2199+ a , b = tee ('abc' )
2200+ take (2 , a )
2201+ take (1 , b )
2202+ self .assertEqual (list (copy .copy (a )), ans [2 :])
2203+ self .assertEqual (list (copy .copy (b )), ans [1 :])
2204+ self .assertEqual (list (a ), ans [2 :])
2205+ self .assertEqual (list (b ), ans [1 :])
2206+ a , b = tee (range (10000 ))
2207+ take (100 , a )
2208+ take (60 , b )
2209+ self .assertEqual (list (copy .copy (a )), long_ans [100 :])
2210+ self .assertEqual (list (copy .copy (b )), long_ans [60 :])
2211+ self .assertEqual (list (a ), long_ans [100 :])
2212+ self .assertEqual (list (b ), long_ans [60 :])
2213+
2214+ # Issue 13454: Crash when deleting backward iterator from tee()
2215+ forward , backward = tee (repeat (None , 2000 )) # 20000000
2216+ try :
2217+ any (forward ) # exhaust the iterator
2218+ del backward
2219+ except :
2220+ del forward , backward
2221+ raise
2222+
2223+
20652224class TestGC (unittest .TestCase ):
20662225
20672226 def makecycle (self , iterator , container ):
0 commit comments