test_genexps.py 7.3 KB

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