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