(root)/
Python-3.12.0/
Lib/
test/
test_genexps.py
       1  import sys
       2  import doctest
       3  import unittest
       4  
       5  
       6  doctests = """
       7  
       8  Test simple loop with conditional
       9  
      10      >>> sum(i*i for i in range(100) if i&1 == 1)
      11      166650
      12  
      13  Test simple nesting
      14  
      15      >>> list((i,j) for i in range(3) for j in range(4) )
      16      [(0, 0), (0, 1), (0, 2), (0, 3), (1, 0), (1, 1), (1, 2), (1, 3), (2, 0), (2, 1), (2, 2), (2, 3)]
      17  
      18  Test nesting with the inner expression dependent on the outer
      19  
      20      >>> list((i,j) for i in range(4) for j in range(i) )
      21      [(1, 0), (2, 0), (2, 1), (3, 0), (3, 1), (3, 2)]
      22  
      23  Test the idiom for temporary variable assignment in comprehensions.
      24  
      25      >>> list((j*j for i in range(4) for j in [i+1]))
      26      [1, 4, 9, 16]
      27      >>> list((j*k for i in range(4) for j in [i+1] for k in [j+1]))
      28      [2, 6, 12, 20]
      29      >>> list((j*k for i in range(4) for j, k in [(i+1, i+2)]))
      30      [2, 6, 12, 20]
      31  
      32  Not assignment
      33  
      34      >>> list((i*i for i in [*range(4)]))
      35      [0, 1, 4, 9]
      36      >>> list((i*i for i in (*range(4),)))
      37      [0, 1, 4, 9]
      38  
      39  Make sure the induction variable is not exposed
      40  
      41      >>> i = 20
      42      >>> sum(i*i for i in range(100))
      43      328350
      44      >>> i
      45      20
      46  
      47  Test first class
      48  
      49      >>> g = (i*i for i in range(4))
      50      >>> type(g)
      51      <class 'generator'>
      52      >>> list(g)
      53      [0, 1, 4, 9]
      54  
      55  Test direct calls to next()
      56  
      57      >>> g = (i*i for i in range(3))
      58      >>> next(g)
      59      0
      60      >>> next(g)
      61      1
      62      >>> next(g)
      63      4
      64      >>> next(g)
      65      Traceback (most recent call last):
      66        File "<pyshell#21>", line 1, in -toplevel-
      67          next(g)
      68      StopIteration
      69  
      70  Does it stay stopped?
      71  
      72      >>> next(g)
      73      Traceback (most recent call last):
      74        File "<pyshell#21>", line 1, in -toplevel-
      75          next(g)
      76      StopIteration
      77      >>> list(g)
      78      []
      79  
      80  Test running gen when defining function is out of scope
      81  
      82      >>> def f(n):
      83      ...     return (i*i for i in range(n))
      84      >>> list(f(10))
      85      [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
      86  
      87      >>> def f(n):
      88      ...     return ((i,j) for i in range(3) for j in range(n))
      89      >>> list(f(4))
      90      [(0, 0), (0, 1), (0, 2), (0, 3), (1, 0), (1, 1), (1, 2), (1, 3), (2, 0), (2, 1), (2, 2), (2, 3)]
      91      >>> def f(n):
      92      ...     return ((i,j) for i in range(3) for j in range(4) if j in range(n))
      93      >>> list(f(4))
      94      [(0, 0), (0, 1), (0, 2), (0, 3), (1, 0), (1, 1), (1, 2), (1, 3), (2, 0), (2, 1), (2, 2), (2, 3)]
      95      >>> list(f(2))
      96      [(0, 0), (0, 1), (1, 0), (1, 1), (2, 0), (2, 1)]
      97  
      98  Verify that parenthesis are required in a statement
      99  
     100      >>> def f(n):
     101      ...     return i*i for i in range(n)
     102      Traceback (most recent call last):
     103         ...
     104      SyntaxError: invalid syntax
     105  
     106  Verify that parenthesis are required when used as a keyword argument value
     107  
     108      >>> dict(a = i for i in range(10))
     109      Traceback (most recent call last):
     110         ...
     111      SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='?
     112  
     113  Verify that parenthesis are required when used as a keyword argument value
     114  
     115      >>> dict(a = (i for i in range(10))) #doctest: +ELLIPSIS
     116      {'a': <generator object <genexpr> at ...>}
     117  
     118  Verify early binding for the outermost for-expression
     119  
     120      >>> x=10
     121      >>> g = (i*i for i in range(x))
     122      >>> x = 5
     123      >>> list(g)
     124      [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
     125  
     126  Verify that the outermost for-expression makes an immediate check
     127  for iterability
     128  
     129      >>> (i for i in 6)
     130      Traceback (most recent call last):
     131        File "<pyshell#4>", line 1, in -toplevel-
     132          (i for i in 6)
     133      TypeError: 'int' object is not iterable
     134  
     135  Verify late binding for the outermost if-expression
     136  
     137      >>> include = (2,4,6,8)
     138      >>> g = (i*i for i in range(10) if i in include)
     139      >>> include = (1,3,5,7,9)
     140      >>> list(g)
     141      [1, 9, 25, 49, 81]
     142  
     143  Verify late binding for the innermost for-expression
     144  
     145      >>> g = ((i,j) for i in range(3) for j in range(x))
     146      >>> x = 4
     147      >>> list(g)
     148      [(0, 0), (0, 1), (0, 2), (0, 3), (1, 0), (1, 1), (1, 2), (1, 3), (2, 0), (2, 1), (2, 2), (2, 3)]
     149  
     150  Verify re-use of tuples (a side benefit of using genexps over listcomps)
     151  
     152      >>> tupleids = list(map(id, ((i,i) for i in range(10))))
     153      >>> int(max(tupleids) - min(tupleids))
     154      0
     155  
     156  Verify that syntax error's are raised for genexps used as lvalues
     157  
     158      >>> (y for y in (1,2)) = 10
     159      Traceback (most recent call last):
     160         ...
     161      SyntaxError: cannot assign to generator expression
     162  
     163      >>> (y for y in (1,2)) += 10
     164      Traceback (most recent call last):
     165         ...
     166      SyntaxError: 'generator expression' is an illegal expression for augmented assignment
     167  
     168  
     169  ########### Tests borrowed from or inspired by test_generators.py ############
     170  
     171  Make a generator that acts like range()
     172  
     173      >>> yrange = lambda n:  (i for i in range(n))
     174      >>> list(yrange(10))
     175      [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
     176  
     177  Generators always return to the most recent caller:
     178  
     179      >>> def creator():
     180      ...     r = yrange(5)
     181      ...     print("creator", next(r))
     182      ...     return r
     183      >>> def caller():
     184      ...     r = creator()
     185      ...     for i in r:
     186      ...             print("caller", i)
     187      >>> caller()
     188      creator 0
     189      caller 1
     190      caller 2
     191      caller 3
     192      caller 4
     193  
     194  Generators can call other generators:
     195  
     196      >>> def zrange(n):
     197      ...     for i in yrange(n):
     198      ...         yield i
     199      >>> list(zrange(5))
     200      [0, 1, 2, 3, 4]
     201  
     202  
     203  Verify that a gen exp cannot be resumed while it is actively running:
     204  
     205      >>> g = (next(me) for i in range(10))
     206      >>> me = g
     207      >>> next(me)
     208      Traceback (most recent call last):
     209        File "<pyshell#30>", line 1, in -toplevel-
     210          next(me)
     211        File "<pyshell#28>", line 1, in <generator expression>
     212          g = (next(me) for i in range(10))
     213      ValueError: generator already executing
     214  
     215  Verify exception propagation
     216  
     217      >>> g = (10 // i for i in (5, 0, 2))
     218      >>> next(g)
     219      2
     220      >>> next(g)
     221      Traceback (most recent call last):
     222        File "<pyshell#37>", line 1, in -toplevel-
     223          next(g)
     224        File "<pyshell#35>", line 1, in <generator expression>
     225          g = (10 // i for i in (5, 0, 2))
     226      ZeroDivisionError: integer division or modulo by zero
     227      >>> next(g)
     228      Traceback (most recent call last):
     229        File "<pyshell#38>", line 1, in -toplevel-
     230          next(g)
     231      StopIteration
     232  
     233  Make sure that None is a valid return value
     234  
     235      >>> list(None for i in range(10))
     236      [None, None, None, None, None, None, None, None, None, None]
     237  
     238  Check that generator attributes are present
     239  
     240      >>> g = (i*i for i in range(3))
     241      >>> expected = set(['gi_frame', 'gi_running'])
     242      >>> set(attr for attr in dir(g) if not attr.startswith('__')) >= expected
     243      True
     244  
     245      >>> from test.support import HAVE_DOCSTRINGS
     246      >>> print(g.__next__.__doc__ if HAVE_DOCSTRINGS else 'Implement next(self).')
     247      Implement next(self).
     248      >>> import types
     249      >>> isinstance(g, types.GeneratorType)
     250      True
     251  
     252  Check the __iter__ slot is defined to return self
     253  
     254      >>> iter(g) is g
     255      True
     256  
     257  Verify that the running flag is set properly
     258  
     259      >>> g = (me.gi_running for i in (0,1))
     260      >>> me = g
     261      >>> me.gi_running
     262      0
     263      >>> next(me)
     264      1
     265      >>> me.gi_running
     266      0
     267  
     268  Verify that genexps are weakly referencable
     269  
     270      >>> import weakref
     271      >>> g = (i*i for i in range(4))
     272      >>> wr = weakref.ref(g)
     273      >>> wr() is g
     274      True
     275      >>> p = weakref.proxy(g)
     276      >>> list(p)
     277      [0, 1, 4, 9]
     278  
     279  
     280  """
     281  
     282  # Trace function can throw off the tuple reuse test.
     283  if hasattr(sys, 'gettrace') and sys.gettrace():
     284      __test__ = {}
     285  else:
     286      __test__ = {'doctests' : doctests}
     287  
     288  def load_tests(loader, tests, pattern):
     289      tests.addTest(doctest.DocTestSuite())
     290      return tests
     291  
     292  
     293  if __name__ == "__main__":
     294      unittest.main()