Skip to content

Commit 27419f1

Browse files
authored
Update tests for the itertools docs rough equivalents (#120509)
1 parent 41554ef commit 27419f1

File tree

1 file changed

+315
-18
lines changed

1 file changed

+315
-18
lines changed

Lib/test/test_itertools.py

+315-18
Original file line numberDiff line numberDiff line change
@@ -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+
16301927
class TestGC(unittest.TestCase):
16311928

16321929
def makecycle(self, iterator, container):

0 commit comments

Comments
 (0)