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