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