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