[Python-checkins] Move doctests to the main docs. Eliminate duplication. Improve coverage. (GH-30869)

rhettinger webhook-mailer at python.org
Tue Jan 25 07:57:02 EST 2022


https://github.com/python/cpython/commit/ee60550e9ba3ab94ca43a890cf4313b63ffa1a81
commit: ee60550e9ba3ab94ca43a890cf4313b63ffa1a81
branch: main
author: Raymond Hettinger <rhettinger at users.noreply.github.com>
committer: rhettinger <rhettinger at users.noreply.github.com>
date: 2022-01-25T06:56:53-06:00
summary:

Move doctests to the main docs. Eliminate duplication. Improve coverage. (GH-30869)

files:
M Doc/library/itertools.rst
M Lib/test/test_itertools.py

diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst
index 34667561c3cfe..f0d93ebb6b21a 100644
--- a/Doc/library/itertools.rst
+++ b/Doc/library/itertools.rst
@@ -1004,3 +1004,241 @@ which incur interpreter overhead.
                c, n = c*(n-r)//n, n-1
            result.append(pool[-1-n])
        return tuple(result)
+
+.. doctest::
+    :hide:
+
+    These examples no longer appear in the docs but are guaranteed
+    to keep working.
+
+    >>> amounts = [120.15, 764.05, 823.14]
+    >>> for checknum, amount in zip(count(1200), amounts):
+    ...     print('Check %d is for $%.2f' % (checknum, amount))
+    ...
+    Check 1200 is for $120.15
+    Check 1201 is for $764.05
+    Check 1202 is for $823.14
+
+    >>> import operator
+    >>> for cube in map(operator.pow, range(1,4), repeat(3)):
+    ...    print(cube)
+    ...
+    1
+    8
+    27
+
+    >>> reportlines = ['EuroPython', 'Roster', '', 'alex', '', 'laura', '', 'martin', '', 'walter', '', 'samuele']
+    >>> for name in islice(reportlines, 3, None, 2):
+    ...    print(name.title())
+    ...
+    Alex
+    Laura
+    Martin
+    Walter
+    Samuele
+
+    >>> from operator import itemgetter
+    >>> d = dict(a=1, b=2, c=1, d=2, e=1, f=2, g=3)
+    >>> di = sorted(sorted(d.items()), key=itemgetter(1))
+    >>> for k, g in groupby(di, itemgetter(1)):
+    ...     print(k, list(map(itemgetter(0), g)))
+    ...
+    1 ['a', 'c', 'e']
+    2 ['b', 'd', 'f']
+    3 ['g']
+
+    # Find runs of consecutive numbers using groupby.  The key to the solution
+    # is differencing with a range so that consecutive numbers all appear in
+    # same group.
+    >>> data = [ 1,  4,5,6, 10, 15,16,17,18, 22, 25,26,27,28]
+    >>> for k, g in groupby(enumerate(data), lambda t:t[0]-t[1]):
+    ...     print(list(map(operator.itemgetter(1), g)))
+    ...
+    [1]
+    [4, 5, 6]
+    [10]
+    [15, 16, 17, 18]
+    [22]
+    [25, 26, 27, 28]
+
+    Now, we test all of the itertool recipes
+
+    >>> import operator
+    >>> import collections
+
+    >>> take(10, count())
+    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+
+    >>> list(prepend(1, [2, 3, 4]))
+    [1, 2, 3, 4]
+
+    >>> list(enumerate('abc'))
+    [(0, 'a'), (1, 'b'), (2, 'c')]
+
+    >>> list(islice(tabulate(lambda x: 2*x), 4))
+    [0, 2, 4, 6]
+
+    >>> list(tail(3, 'ABCDEFG'))
+    ['E', 'F', 'G']
+
+    >>> it = iter(range(10))
+    >>> consume(it, 3)
+    >>> next(it)
+    3
+    >>> consume(it)
+    >>> next(it, 'Done')
+    'Done'
+
+    >>> nth('abcde', 3)
+    'd'
+
+    >>> nth('abcde', 9) is None
+    True
+
+    >>> [all_equal(s) for s in ('', 'A', 'AAAA', 'AAAB', 'AAABA')]
+    [True, True, True, False, False]
+
+    >>> quantify(range(99), lambda x: x%2==0)
+    50
+
+    >>> quantify([True, False, False, True, True])
+    3
+
+    >>> quantify(range(12), pred=lambda x: x%2==1)
+    6
+
+    >>> a = [[1, 2, 3], [4, 5, 6]]
+    >>> list(flatten(a))
+    [1, 2, 3, 4, 5, 6]
+
+    >>> list(repeatfunc(pow, 5, 2, 3))
+    [8, 8, 8, 8, 8]
+
+    >>> import random
+    >>> take(5, map(int, repeatfunc(random.random)))
+    [0, 0, 0, 0, 0]
+
+    >>> list(islice(pad_none('abc'), 0, 6))
+    ['a', 'b', 'c', None, None, None]
+
+    >>> list(ncycles('abc', 3))
+    ['a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c']
+
+    >>> dotproduct([1,2,3], [4,5,6])
+    32
+
+    >>> data = [20, 40, 24, 32, 20, 28, 16]
+    >>> list(convolve(data, [0.25, 0.25, 0.25, 0.25]))
+    [5.0, 15.0, 21.0, 29.0, 29.0, 26.0, 24.0, 16.0, 11.0, 4.0]
+    >>> list(convolve(data, [1, -1]))
+    [20, 20, -16, 8, -12, 8, -12, -16]
+    >>> list(convolve(data, [1, -2, 1]))
+    [20, 0, -36, 24, -20, 20, -20, -4, 16]
+
+    >>> list(flatten([('a', 'b'), (), ('c', 'd', 'e'), ('f',), ('g', 'h', 'i')]))
+    ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
+
+    >>> import random
+    >>> random.seed(85753098575309)
+    >>> list(repeatfunc(random.random, 3))
+    [0.16370491282496968, 0.45889608687313455, 0.3747076837820118]
+    >>> list(repeatfunc(chr, 3, 65))
+    ['A', 'A', 'A']
+    >>> list(repeatfunc(pow, 3, 2, 5))
+    [32, 32, 32]
+
+    >>> list(grouper('abcdefg', 3, fillvalue='x'))
+    [('a', 'b', 'c'), ('d', 'e', 'f'), ('g', 'x', 'x')]
+
+    >>> it = grouper('abcdefg', 3, incomplete='strict')
+    >>> next(it)
+    ('a', 'b', 'c')
+    >>> next(it)
+    ('d', 'e', 'f')
+    >>> next(it)
+    Traceback (most recent call last):
+      ...
+    ValueError: zip() argument 2 is shorter than argument 1
+
+    >>> list(grouper('abcdefg', n=3, incomplete='ignore'))
+    [('a', 'b', 'c'), ('d', 'e', 'f')]
+
+    >>> list(triplewise('ABCDEFG'))
+    [('A', 'B', 'C'), ('B', 'C', 'D'), ('C', 'D', 'E'), ('D', 'E', 'F'), ('E', 'F', 'G')]
+
+    >>> list(sliding_window('ABCDEFG', 4))
+    [('A', 'B', 'C', 'D'), ('B', 'C', 'D', 'E'), ('C', 'D', 'E', 'F'), ('D', 'E', 'F', 'G')]
+
+    >>> list(roundrobin('abc', 'd', 'ef'))
+    ['a', 'd', 'e', 'b', 'f', 'c']
+
+    >>> def is_odd(x):
+    ...     return x % 2 == 1
+
+    >>> evens, odds = partition(is_odd, range(10))
+    >>> list(evens)
+    [0, 2, 4, 6, 8]
+    >>> list(odds)
+    [1, 3, 5, 7, 9]
+
+    >>> it = iter('ABCdEfGhI')
+    >>> all_upper, remainder = before_and_after(str.isupper, it)
+    >>> ''.join(all_upper)
+    'ABC'
+    >>> ''.join(remainder)
+    'dEfGhI'
+
+    >>> list(powerset([1,2,3]))
+    [(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)]
+
+    >>> all(len(list(powerset(range(n)))) == 2**n for n in range(18))
+    True
+
+    >>> list(powerset('abcde')) == sorted(sorted(set(powerset('abcde'))), key=len)
+    True
+
+    >>> list(unique_everseen('AAAABBBCCDAABBB'))
+    ['A', 'B', 'C', 'D']
+
+    >>> list(unique_everseen('ABBCcAD', str.lower))
+    ['A', 'B', 'C', 'D']
+
+    >>> list(unique_justseen('AAAABBBCCDAABBB'))
+    ['A', 'B', 'C', 'D', 'A', 'B']
+
+    >>> list(unique_justseen('ABBCcAD', str.lower))
+    ['A', 'B', 'C', 'A', 'D']
+
+    >>> d = dict(a=1, b=2, c=3)
+    >>> it = iter_except(d.popitem, KeyError)
+    >>> d['d'] = 4
+    >>> next(it)
+    ('d', 4)
+    >>> next(it)
+    ('c', 3)
+    >>> next(it)
+    ('b', 2)
+    >>> d['e'] = 5
+    >>> next(it)
+    ('e', 5)
+    >>> next(it)
+    ('a', 1)
+    >>> next(it, 'empty')
+    'empty'
+
+    >>> first_true('ABC0DEF1', '9', str.isdigit)
+    '0'
+
+    >>> population = 'ABCDEFGH'
+    >>> for r in range(len(population) + 1):
+    ...     seq = list(combinations(population, r))
+    ...     for i in range(len(seq)):
+    ...         assert nth_combination(population, r, i) == seq[i]
+    ...     for i in range(-len(seq), 0):
+    ...         assert nth_combination(population, r, i) == seq[i]
+
+    >>> iterable = 'abcde'
+    >>> r = 3
+    >>> combos = list(combinations(iterable, r))
+    >>> all(nth_combination(iterable, r, i) == comb for i, comb in enumerate(combos))
+    True
diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py
index 3043e8c404e6e..3f3f7cb35d0bc 100644
--- a/Lib/test/test_itertools.py
+++ b/Lib/test/test_itertools.py
@@ -2321,399 +2321,6 @@ def test_permutations_sizeof(self):
               basesize + 10 * self.ssize_t + 4 * self.ssize_t)
 
 
-libreftest = """ Doctest for examples in the library reference: libitertools.tex
-
-
->>> amounts = [120.15, 764.05, 823.14]
->>> for checknum, amount in zip(count(1200), amounts):
-...     print('Check %d is for $%.2f' % (checknum, amount))
-...
-Check 1200 is for $120.15
-Check 1201 is for $764.05
-Check 1202 is for $823.14
-
->>> import operator
->>> for cube in map(operator.pow, range(1,4), repeat(3)):
-...    print(cube)
-...
-1
-8
-27
-
->>> reportlines = ['EuroPython', 'Roster', '', 'alex', '', 'laura', '', 'martin', '', 'walter', '', 'samuele']
->>> for name in islice(reportlines, 3, None, 2):
-...    print(name.title())
-...
-Alex
-Laura
-Martin
-Walter
-Samuele
-
->>> from operator import itemgetter
->>> d = dict(a=1, b=2, c=1, d=2, e=1, f=2, g=3)
->>> di = sorted(sorted(d.items()), key=itemgetter(1))
->>> for k, g in groupby(di, itemgetter(1)):
-...     print(k, list(map(itemgetter(0), g)))
-...
-1 ['a', 'c', 'e']
-2 ['b', 'd', 'f']
-3 ['g']
-
-# Find runs of consecutive numbers using groupby.  The key to the solution
-# is differencing with a range so that consecutive numbers all appear in
-# same group.
->>> data = [ 1,  4,5,6, 10, 15,16,17,18, 22, 25,26,27,28]
->>> for k, g in groupby(enumerate(data), lambda t:t[0]-t[1]):
-...     print(list(map(operator.itemgetter(1), g)))
-...
-[1]
-[4, 5, 6]
-[10]
-[15, 16, 17, 18]
-[22]
-[25, 26, 27, 28]
-
->>> def take(n, iterable):
-...     "Return first n items of the iterable as a list"
-...     return list(islice(iterable, n))
-
->>> def prepend(value, iterator):
-...     "Prepend a single value in front of an iterator"
-...     # prepend(1, [2, 3, 4]) -> 1 2 3 4
-...     return chain([value], iterator)
-
->>> def enumerate(iterable, start=0):
-...     return zip(count(start), iterable)
-
->>> def tabulate(function, start=0):
-...     "Return function(0), function(1), ..."
-...     return map(function, count(start))
-
->>> import collections
->>> def consume(iterator, n=None):
-...     "Advance the iterator n-steps ahead. If n is None, consume entirely."
-...     # Use functions that consume iterators at C speed.
-...     if n is None:
-...         # feed the entire iterator into a zero-length deque
-...         collections.deque(iterator, maxlen=0)
-...     else:
-...         # advance to the empty slice starting at position n
-...         next(islice(iterator, n, n), None)
-
->>> def nth(iterable, n, default=None):
-...     "Returns the nth item or a default value"
-...     return next(islice(iterable, n, None), default)
-
->>> def all_equal(iterable):
-...     "Returns True if all the elements are equal to each other"
-...     g = groupby(iterable)
-...     return next(g, True) and not next(g, False)
-
->>> def quantify(iterable, pred=bool):
-...     "Count how many times the predicate is true"
-...     return sum(map(pred, iterable))
-
->>> def pad_none(iterable):
-...     "Returns the sequence elements and then returns None indefinitely"
-...     return chain(iterable, repeat(None))
-
->>> def ncycles(iterable, n):
-...     "Returns the sequence elements n times"
-...     return chain(*repeat(iterable, n))
-
->>> def dotproduct(vec1, vec2):
-...     return sum(map(operator.mul, vec1, vec2))
-
->>> def flatten(listOfLists):
-...     return list(chain.from_iterable(listOfLists))
-
->>> def repeatfunc(func, times=None, *args):
-...     "Repeat calls to func with specified arguments."
-...     "   Example:  repeatfunc(random.random)"
-...     if times is None:
-...         return starmap(func, repeat(args))
-...     else:
-...         return starmap(func, repeat(args, times))
-
->>> def grouper(iterable, n, *, incomplete='fill', fillvalue=None):
-...     "Collect data into non-overlapping fixed-length chunks or blocks"
-...     # grouper('ABCDEFG', 3, fillvalue='x') --> ABC DEF Gxx
-...     # grouper('ABCDEFG', 3, incomplete='strict') --> ABC DEF ValueError
-...     # grouper('ABCDEFG', 3, incomplete='ignore') --> ABC DEF
-...     args = [iter(iterable)] * n
-...     if incomplete == 'fill':
-...         return zip_longest(*args, fillvalue=fillvalue)
-...     if incomplete == 'strict':
-...         return zip(*args, strict=True)
-...     if incomplete == 'ignore':
-...         return zip(*args)
-...     else:
-...         raise ValueError('Expected fill, strict, or ignore')
-
->>> def triplewise(iterable):
-...     "Return overlapping triplets from an iterable"
-...     # pairwise('ABCDEFG') -> ABC BCD CDE DEF EFG
-...     for (a, _), (b, c) in pairwise(pairwise(iterable)):
-...         yield a, b, c
-
->>> import collections
->>> def sliding_window(iterable, n):
-...     # sliding_window('ABCDEFG', 4) -> ABCD BCDE CDEF DEFG
-...     it = iter(iterable)
-...     window = collections.deque(islice(it, n), maxlen=n)
-...     if len(window) == n:
-...         yield tuple(window)
-...     for x in it:
-...         window.append(x)
-...         yield tuple(window)
-
->>> def roundrobin(*iterables):
-...     "roundrobin('ABC', 'D', 'EF') --> A D E B F C"
-...     # Recipe credited to George Sakkis
-...     pending = len(iterables)
-...     nexts = cycle(iter(it).__next__ for it in iterables)
-...     while pending:
-...         try:
-...             for next in nexts:
-...                 yield next()
-...         except StopIteration:
-...             pending -= 1
-...             nexts = cycle(islice(nexts, pending))
-
->>> def partition(pred, iterable):
-...     "Use a predicate to partition entries into false entries and true entries"
-...     # partition(is_odd, range(10)) --> 0 2 4 6 8   and  1 3 5 7 9
-...     t1, t2 = tee(iterable)
-...     return filterfalse(pred, t1), filter(pred, t2)
-
->>> def before_and_after(predicate, it):
-...     ''' Variant of takewhile() that allows complete
-...         access to the remainder of the iterator.
-...
-...         >>> all_upper, remainder = before_and_after(str.isupper, 'ABCdEfGhI')
-...         >>> str.join('', all_upper)
-...         'ABC'
-...         >>> str.join('', remainder)
-...         'dEfGhI'
-...
-...         Note that the first iterator must be fully
-...         consumed before the second iterator can
-...         generate valid results.
-...     '''
-...     it = iter(it)
-...     transition = []
-...     def true_iterator():
-...         for elem in it:
-...             if predicate(elem):
-...                 yield elem
-...             else:
-...                 transition.append(elem)
-...                 return
-...     def remainder_iterator():
-...         yield from transition
-...         yield from it
-...     return true_iterator(), remainder_iterator()
-
->>> def powerset(iterable):
-...     "powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
-...     s = list(iterable)
-...     return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))
-
->>> def unique_everseen(iterable, key=None):
-...     "List unique elements, preserving order. Remember all elements ever seen."
-...     # unique_everseen('AAAABBBCCDAABBB') --> A B C D
-...     # unique_everseen('ABBCcAD', str.lower) --> A B C D
-...     seen = set()
-...     seen_add = seen.add
-...     if key is None:
-...         for element in iterable:
-...             if element not in seen:
-...                 seen_add(element)
-...                 yield element
-...     else:
-...         for element in iterable:
-...             k = key(element)
-...             if k not in seen:
-...                 seen_add(k)
-...                 yield element
-
->>> def unique_justseen(iterable, key=None):
-...     "List unique elements, preserving order. Remember only the element just seen."
-...     # unique_justseen('AAAABBBCCDAABBB') --> A B C D A B
-...     # unique_justseen('ABBCcAD', str.lower) --> A B C A D
-...     return map(next, map(itemgetter(1), groupby(iterable, key)))
-
->>> def first_true(iterable, default=False, pred=None):
-...     '''Returns the first true value in the iterable.
-...
-...     If no true value is found, returns *default*
-...
-...     If *pred* is not None, returns the first item
-...     for which pred(item) is true.
-...
-...     '''
-...     # first_true([a,b,c], x) --> a or b or c or x
-...     # first_true([a,b], x, f) --> a if f(a) else b if f(b) else x
-...     return next(filter(pred, iterable), default)
-
->>> def nth_combination(iterable, r, index):
-...     'Equivalent to list(combinations(iterable, r))[index]'
-...     pool = tuple(iterable)
-...     n = len(pool)
-...     if r < 0 or r > n:
-...         raise ValueError
-...     c = 1
-...     k = min(r, n-r)
-...     for i in range(1, k+1):
-...         c = c * (n - k + i) // i
-...     if index < 0:
-...         index += c
-...     if index < 0 or index >= c:
-...         raise IndexError
-...     result = []
-...     while r:
-...         c, n, r = c*r//n, n-1, r-1
-...         while index >= c:
-...             index -= c
-...             c, n = c*(n-r)//n, n-1
-...         result.append(pool[-1-n])
-...     return tuple(result)
-
-
-This is not part of the examples but it tests to make sure the definitions
-perform as purported.
-
->>> take(10, count())
-[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
-
->>> list(prepend(1, [2, 3, 4]))
-[1, 2, 3, 4]
-
->>> list(enumerate('abc'))
-[(0, 'a'), (1, 'b'), (2, 'c')]
-
->>> list(islice(tabulate(lambda x: 2*x), 4))
-[0, 2, 4, 6]
-
->>> it = iter(range(10))
->>> consume(it, 3)
->>> next(it)
-3
->>> consume(it)
->>> next(it, 'Done')
-'Done'
-
->>> nth('abcde', 3)
-'d'
-
->>> nth('abcde', 9) is None
-True
-
->>> [all_equal(s) for s in ('', 'A', 'AAAA', 'AAAB', 'AAABA')]
-[True, True, True, False, False]
-
->>> quantify(range(99), lambda x: x%2==0)
-50
-
->>> a = [[1, 2, 3], [4, 5, 6]]
->>> flatten(a)
-[1, 2, 3, 4, 5, 6]
-
->>> list(repeatfunc(pow, 5, 2, 3))
-[8, 8, 8, 8, 8]
-
->>> import random
->>> take(5, map(int, repeatfunc(random.random)))
-[0, 0, 0, 0, 0]
-
->>> list(islice(pad_none('abc'), 0, 6))
-['a', 'b', 'c', None, None, None]
-
->>> list(ncycles('abc', 3))
-['a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c']
-
->>> dotproduct([1,2,3], [4,5,6])
-32
-
->>> list(grouper('abcdefg', 3, fillvalue='x'))
-[('a', 'b', 'c'), ('d', 'e', 'f'), ('g', 'x', 'x')]
-
->>> it = grouper('abcdefg', 3, incomplete='strict')
->>> next(it)
-('a', 'b', 'c')
->>> next(it)
-('d', 'e', 'f')
->>> next(it)
-Traceback (most recent call last):
-  ...
-ValueError: zip() argument 2 is shorter than argument 1
-
->>> list(grouper('abcdefg', n=3, incomplete='ignore'))
-[('a', 'b', 'c'), ('d', 'e', 'f')]
-
->>> list(triplewise('ABCDEFG'))
-[('A', 'B', 'C'), ('B', 'C', 'D'), ('C', 'D', 'E'), ('D', 'E', 'F'), ('E', 'F', 'G')]
-
->>> list(sliding_window('ABCDEFG', 4))
-[('A', 'B', 'C', 'D'), ('B', 'C', 'D', 'E'), ('C', 'D', 'E', 'F'), ('D', 'E', 'F', 'G')]
-
->>> list(roundrobin('abc', 'd', 'ef'))
-['a', 'd', 'e', 'b', 'f', 'c']
-
->>> def is_odd(x):
-...     return x % 2 == 1
-
->>> evens, odds = partition(is_odd, range(10))
->>> list(evens)
-[0, 2, 4, 6, 8]
->>> list(odds)
-[1, 3, 5, 7, 9]
-
->>> it = iter('ABCdEfGhI')
->>> all_upper, remainder = before_and_after(str.isupper, it)
->>> ''.join(all_upper)
-'ABC'
->>> ''.join(remainder)
-'dEfGhI'
-
->>> list(powerset([1,2,3]))
-[(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)]
-
->>> all(len(list(powerset(range(n)))) == 2**n for n in range(18))
-True
-
->>> list(powerset('abcde')) == sorted(sorted(set(powerset('abcde'))), key=len)
-True
-
->>> list(unique_everseen('AAAABBBCCDAABBB'))
-['A', 'B', 'C', 'D']
-
->>> list(unique_everseen('ABBCcAD', str.lower))
-['A', 'B', 'C', 'D']
-
->>> list(unique_justseen('AAAABBBCCDAABBB'))
-['A', 'B', 'C', 'D', 'A', 'B']
-
->>> list(unique_justseen('ABBCcAD', str.lower))
-['A', 'B', 'C', 'A', 'D']
-
->>> first_true('ABC0DEF1', '9', str.isdigit)
-'0'
-
->>> population = 'ABCDEFGH'
->>> for r in range(len(population) + 1):
-...     seq = list(combinations(population, r))
-...     for i in range(len(seq)):
-...         assert nth_combination(population, r, i) == seq[i]
-...     for i in range(-len(seq), 0):
-...         assert nth_combination(population, r, i) == seq[i]
-
-
-"""
-
-__test__ = {'libreftest' : libreftest}
-
 def load_tests(loader, tests, pattern):
     tests.addTest(doctest.DocTestSuite())
     return tests



More information about the Python-checkins mailing list