@@ -1587,27 +1587,169 @@ def batched_recipe(iterable, n):
15871587                self .assertEqual (r1 , r2 )
15881588                self .assertEqual (e1 , e2 )
15891589
1590+ 
1591+     def  test_groupby_recipe (self ):
1592+ 
1593+         # Begin groupby() recipe ####################################### 
1594+ 
1595+         def  groupby (iterable , key = None ):
1596+             # [k for k, g in groupby('AAAABBBCCDAABBB')] → A B C D A B 
1597+             # [list(g) for k, g in groupby('AAAABBBCCD')] → AAAA BBB CC D 
1598+ 
1599+             keyfunc  =  (lambda  x : x ) if  key  is  None  else  key 
1600+             iterator  =  iter (iterable )
1601+             exhausted  =  False 
1602+ 
1603+             def  _grouper (target_key ):
1604+                 nonlocal  curr_value , curr_key , exhausted 
1605+                 yield  curr_value 
1606+                 for  curr_value  in  iterator :
1607+                     curr_key  =  keyfunc (curr_value )
1608+                     if  curr_key  !=  target_key :
1609+                         return 
1610+                     yield  curr_value 
1611+                 exhausted  =  True 
1612+ 
1613+             try :
1614+                 curr_value  =  next (iterator )
1615+             except  StopIteration :
1616+                 return 
1617+             curr_key  =  keyfunc (curr_value )
1618+ 
1619+             while  not  exhausted :
1620+                 target_key  =  curr_key 
1621+                 curr_group  =  _grouper (target_key )
1622+                 yield  curr_key , curr_group 
1623+                 if  curr_key  ==  target_key :
1624+                     for  _  in  curr_group :
1625+                         pass 
1626+ 
1627+         # End groupby() recipe ######################################### 
1628+ 
1629+         # Check whether it accepts arguments correctly 
1630+         self .assertEqual ([], list (groupby ([])))
1631+         self .assertEqual ([], list (groupby ([], key = id )))
1632+         self .assertRaises (TypeError , list , groupby ('abc' , []))
1633+         if  False :
1634+             # Test not applicable to the recipe 
1635+             self .assertRaises (TypeError , list , groupby ('abc' , None ))
1636+         self .assertRaises (TypeError , groupby , 'abc' , lambda  x :x , 10 )
1637+ 
1638+         # Check normal input 
1639+         s  =  [(0 , 10 , 20 ), (0 , 11 ,21 ), (0 ,12 ,21 ), (1 ,13 ,21 ), (1 ,14 ,22 ),
1640+              (2 ,15 ,22 ), (3 ,16 ,23 ), (3 ,17 ,23 )]
1641+         dup  =  []
1642+         for  k , g  in  groupby (s , lambda  r :r [0 ]):
1643+             for  elem  in  g :
1644+                 self .assertEqual (k , elem [0 ])
1645+                 dup .append (elem )
1646+         self .assertEqual (s , dup )
1647+ 
1648+         # Check nested case 
1649+         dup  =  []
1650+         for  k , g  in  groupby (s , testR ):
1651+             for  ik , ig  in  groupby (g , testR2 ):
1652+                 for  elem  in  ig :
1653+                     self .assertEqual (k , elem [0 ])
1654+                     self .assertEqual (ik , elem [2 ])
1655+                     dup .append (elem )
1656+         self .assertEqual (s , dup )
1657+ 
1658+         # Check case where inner iterator is not used 
1659+         keys  =  [k  for  k , g  in  groupby (s , testR )]
1660+         expectedkeys  =  set ([r [0 ] for  r  in  s ])
1661+         self .assertEqual (set (keys ), expectedkeys )
1662+         self .assertEqual (len (keys ), len (expectedkeys ))
1663+ 
1664+         # Check case where inner iterator is used after advancing the groupby 
1665+         # iterator 
1666+         s  =  list (zip ('AABBBAAAA' , range (9 )))
1667+         it  =  groupby (s , testR )
1668+         _ , g1  =  next (it )
1669+         _ , g2  =  next (it )
1670+         _ , g3  =  next (it )
1671+         self .assertEqual (list (g1 ), [])
1672+         self .assertEqual (list (g2 ), [])
1673+         self .assertEqual (next (g3 ), ('A' , 5 ))
1674+         list (it )  # exhaust the groupby iterator 
1675+         self .assertEqual (list (g3 ), [])
1676+ 
1677+         # Exercise pipes and filters style 
1678+         s  =  'abracadabra' 
1679+         # sort s | uniq 
1680+         r  =  [k  for  k , g  in  groupby (sorted (s ))]
1681+         self .assertEqual (r , ['a' , 'b' , 'c' , 'd' , 'r' ])
1682+         # sort s | uniq -d 
1683+         r  =  [k  for  k , g  in  groupby (sorted (s )) if  list (islice (g ,1 ,2 ))]
1684+         self .assertEqual (r , ['a' , 'b' , 'r' ])
1685+         # sort s | uniq -c 
1686+         r  =  [(len (list (g )), k ) for  k , g  in  groupby (sorted (s ))]
1687+         self .assertEqual (r , [(5 , 'a' ), (2 , 'b' ), (1 , 'c' ), (1 , 'd' ), (2 , 'r' )])
1688+         # sort s | uniq -c | sort -rn | head -3 
1689+         r  =  sorted ([(len (list (g )) , k ) for  k , g  in  groupby (sorted (s ))], reverse = True )[:3 ]
1690+         self .assertEqual (r , [(5 , 'a' ), (2 , 'r' ), (2 , 'b' )])
1691+ 
1692+         # iter.__next__ failure 
1693+         class  ExpectedError (Exception ):
1694+             pass 
1695+         def  delayed_raise (n = 0 ):
1696+             for  i  in  range (n ):
1697+                 yield  'yo' 
1698+             raise  ExpectedError 
1699+         def  gulp (iterable , keyp = None , func = list ):
1700+             return  [func (g ) for  k , g  in  groupby (iterable , keyp )]
1701+ 
1702+         # iter.__next__ failure on outer object 
1703+         self .assertRaises (ExpectedError , gulp , delayed_raise (0 ))
1704+         # iter.__next__ failure on inner object 
1705+         self .assertRaises (ExpectedError , gulp , delayed_raise (1 ))
1706+ 
1707+         # __eq__ failure 
1708+         class  DummyCmp :
1709+             def  __eq__ (self , dst ):
1710+                 raise  ExpectedError 
1711+         s  =  [DummyCmp (), DummyCmp (), None ]
1712+ 
1713+         # __eq__ failure on outer object 
1714+         self .assertRaises (ExpectedError , gulp , s , func = id )
1715+         # __eq__ failure on inner object 
1716+         self .assertRaises (ExpectedError , gulp , s )
1717+ 
1718+         # keyfunc failure 
1719+         def  keyfunc (obj ):
1720+             if  keyfunc .skip  >  0 :
1721+                 keyfunc .skip  -=  1 
1722+                 return  obj 
1723+             else :
1724+                 raise  ExpectedError 
1725+ 
1726+         # keyfunc failure on outer object 
1727+         keyfunc .skip  =  0 
1728+         self .assertRaises (ExpectedError , gulp , [None ], keyfunc )
1729+         keyfunc .skip  =  1 
1730+         self .assertRaises (ExpectedError , gulp , [None , None ], keyfunc )
1731+ 
1732+ 
15901733    @staticmethod  
15911734    def  islice (iterable , * args ):
1735+         # islice('ABCDEFG', 2) → A B 
1736+         # islice('ABCDEFG', 2, 4) → C D 
1737+         # islice('ABCDEFG', 2, None) → C D E F G 
1738+         # islice('ABCDEFG', 0, None, 2) → A C E G 
1739+ 
15921740        s  =  slice (* args )
1593-         start , stop , step  =  s .start  or  0 , s .stop  or  sys .maxsize , s .step  or  1 
1594-         it  =  iter (range (start , stop , step ))
1595-         try :
1596-             nexti  =  next (it )
1597-         except  StopIteration :
1598-             # Consume *iterable* up to the *start* position. 
1599-             for  i , element  in  zip (range (start ), iterable ):
1600-                 pass 
1601-             return 
1602-         try :
1603-             for  i , element  in  enumerate (iterable ):
1604-                 if  i  ==  nexti :
1605-                     yield  element 
1606-                     nexti  =  next (it )
1607-         except  StopIteration :
1608-             # Consume to *stop*. 
1609-             for  i , element  in  zip (range (i  +  1 , stop ), iterable ):
1610-                 pass 
1741+         start  =  0  if  s .start  is  None  else  s .start 
1742+         stop  =  s .stop 
1743+         step  =  1  if  s .step  is  None  else  s .step 
1744+         if  start  <  0  or  (stop  is  not None  and  stop  <  0 ) or  step  <=  0 :
1745+             raise  ValueError 
1746+ 
1747+         indices  =  count () if  stop  is  None  else  range (max (start , stop ))
1748+         next_i  =  start 
1749+         for  i , element  in  zip (indices , iterable ):
1750+             if  i  ==  next_i :
1751+                 yield  element 
1752+                 next_i  +=  step 
16111753
16121754    def  test_islice_recipe (self ):
16131755        self .assertEqual (list (self .islice ('ABCDEFG' , 2 )), list ('AB' ))
@@ -1627,6 +1769,161 @@ def test_islice_recipe(self):
16271769        self .assertEqual (next (c ), 3 )
16281770
16291771
1772+     def  test_tee_recipe (self ):
1773+ 
1774+         # Begin tee() recipe ########################################### 
1775+ 
1776+         def  tee (iterable , n = 2 ):
1777+             iterator  =  iter (iterable )
1778+             shared_link  =  [None , None ]
1779+             return  tuple (_tee (iterator , shared_link ) for  _  in  range (n ))
1780+ 
1781+         def  _tee (iterator , link ):
1782+             try :
1783+                 while  True :
1784+                     if  link [1 ] is  None :
1785+                         link [0 ] =  next (iterator )
1786+                         link [1 ] =  [None , None ]
1787+                     value , link  =  link 
1788+                     yield  value 
1789+             except  StopIteration :
1790+                 return 
1791+ 
1792+         # End tee() recipe ############################################# 
1793+ 
1794+         n  =  200 
1795+ 
1796+         a , b  =  tee ([])        # test empty iterator 
1797+         self .assertEqual (list (a ), [])
1798+         self .assertEqual (list (b ), [])
1799+ 
1800+         a , b  =  tee (irange (n )) # test 100% interleaved 
1801+         self .assertEqual (lzip (a ,b ), lzip (range (n ), range (n )))
1802+ 
1803+         a , b  =  tee (irange (n )) # test 0% interleaved 
1804+         self .assertEqual (list (a ), list (range (n )))
1805+         self .assertEqual (list (b ), list (range (n )))
1806+ 
1807+         a , b  =  tee (irange (n )) # test dealloc of leading iterator 
1808+         for  i  in  range (100 ):
1809+             self .assertEqual (next (a ), i )
1810+         del  a 
1811+         self .assertEqual (list (b ), list (range (n )))
1812+ 
1813+         a , b  =  tee (irange (n )) # test dealloc of trailing iterator 
1814+         for  i  in  range (100 ):
1815+             self .assertEqual (next (a ), i )
1816+         del  b 
1817+         self .assertEqual (list (a ), list (range (100 , n )))
1818+ 
1819+         for  j  in  range (5 ):   # test randomly interleaved 
1820+             order  =  [0 ]* n  +  [1 ]* n 
1821+             random .shuffle (order )
1822+             lists  =  ([], [])
1823+             its  =  tee (irange (n ))
1824+             for  i  in  order :
1825+                 value  =  next (its [i ])
1826+                 lists [i ].append (value )
1827+             self .assertEqual (lists [0 ], list (range (n )))
1828+             self .assertEqual (lists [1 ], list (range (n )))
1829+ 
1830+         # test argument format checking 
1831+         self .assertRaises (TypeError , tee )
1832+         self .assertRaises (TypeError , tee , 3 )
1833+         self .assertRaises (TypeError , tee , [1 ,2 ], 'x' )
1834+         self .assertRaises (TypeError , tee , [1 ,2 ], 3 , 'x' )
1835+ 
1836+         # Tests not applicable to the tee() recipe 
1837+         if  False :
1838+             # tee object should be instantiable 
1839+             a , b  =  tee ('abc' )
1840+             c  =  type (a )('def' )
1841+             self .assertEqual (list (c ), list ('def' ))
1842+ 
1843+         # test long-lagged and multi-way split 
1844+         a , b , c  =  tee (range (2000 ), 3 )
1845+         for  i  in  range (100 ):
1846+             self .assertEqual (next (a ), i )
1847+         self .assertEqual (list (b ), list (range (2000 )))
1848+         self .assertEqual ([next (c ), next (c )], list (range (2 )))
1849+         self .assertEqual (list (a ), list (range (100 ,2000 )))
1850+         self .assertEqual (list (c ), list (range (2 ,2000 )))
1851+ 
1852+         # Tests not applicable to the tee() recipe 
1853+         if  False :
1854+             # test invalid values of n 
1855+             self .assertRaises (TypeError , tee , 'abc' , 'invalid' )
1856+             self .assertRaises (ValueError , tee , [], - 1 )
1857+ 
1858+         for  n  in  range (5 ):
1859+             result  =  tee ('abc' , n )
1860+             self .assertEqual (type (result ), tuple )
1861+             self .assertEqual (len (result ), n )
1862+             self .assertEqual ([list (x ) for  x  in  result ], [list ('abc' )]* n )
1863+ 
1864+ 
1865+         # Tests not applicable to the tee() recipe 
1866+         if  False :
1867+             # tee pass-through to copyable iterator 
1868+             a , b  =  tee ('abc' )
1869+             c , d  =  tee (a )
1870+             self .assertTrue (a  is  c )
1871+ 
1872+             # test tee_new 
1873+             t1 , t2  =  tee ('abc' )
1874+             tnew  =  type (t1 )
1875+             self .assertRaises (TypeError , tnew )
1876+             self .assertRaises (TypeError , tnew , 10 )
1877+             t3  =  tnew (t1 )
1878+             self .assertTrue (list (t1 ) ==  list (t2 ) ==  list (t3 ) ==  list ('abc' ))
1879+ 
1880+         # test that tee objects are weak referencable 
1881+         a , b  =  tee (range (10 ))
1882+         p  =  weakref .proxy (a )
1883+         self .assertEqual (getattr (p , '__class__' ), type (b ))
1884+         del  a 
1885+         gc .collect ()  # For PyPy or other GCs. 
1886+         self .assertRaises (ReferenceError , getattr , p , '__class__' )
1887+ 
1888+         ans  =  list ('abc' )
1889+         long_ans  =  list (range (10000 ))
1890+ 
1891+         # Tests not applicable to the tee() recipe 
1892+         if  False :
1893+             # check copy 
1894+             a , b  =  tee ('abc' )
1895+             self .assertEqual (list (copy .copy (a )), ans )
1896+             self .assertEqual (list (copy .copy (b )), ans )
1897+             a , b  =  tee (list (range (10000 )))
1898+             self .assertEqual (list (copy .copy (a )), long_ans )
1899+             self .assertEqual (list (copy .copy (b )), long_ans )
1900+ 
1901+             # check partially consumed copy 
1902+             a , b  =  tee ('abc' )
1903+             take (2 , a )
1904+             take (1 , b )
1905+             self .assertEqual (list (copy .copy (a )), ans [2 :])
1906+             self .assertEqual (list (copy .copy (b )), ans [1 :])
1907+             self .assertEqual (list (a ), ans [2 :])
1908+             self .assertEqual (list (b ), ans [1 :])
1909+             a , b  =  tee (range (10000 ))
1910+             take (100 , a )
1911+             take (60 , b )
1912+             self .assertEqual (list (copy .copy (a )), long_ans [100 :])
1913+             self .assertEqual (list (copy .copy (b )), long_ans [60 :])
1914+             self .assertEqual (list (a ), long_ans [100 :])
1915+             self .assertEqual (list (b ), long_ans [60 :])
1916+ 
1917+         # Issue 13454: Crash when deleting backward iterator from tee() 
1918+         forward , backward  =  tee (repeat (None , 2000 )) # 20000000 
1919+         try :
1920+             any (forward )  # exhaust the iterator 
1921+             del  backward 
1922+         except :
1923+             del  forward , backward 
1924+             raise 
1925+ 
1926+ 
16301927class  TestGC (unittest .TestCase ):
16311928
16321929    def  makecycle (self , iterator , container ):
0 commit comments