/Lib/test/test_genexps.py

http://unladen-swallow.googlecode.com/ · Python · 280 lines · 274 code · 5 blank · 1 comment · 4 complexity · e24dcaaf5bdab9f37665b95bce47397c MD5 · raw file

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