Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 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 | |
Serhiy Storchaka | 8c579b1 | 2020-02-12 12:18:59 +0200 | [diff] [blame] | 18 | Test 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 | |
| 27 | Not 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 | |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 34 | Make 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 | |
| 42 | Test first class |
| 43 | |
| 44 | >>> g = (i*i for i in range(4)) |
| 45 | >>> type(g) |
Benjamin Peterson | ab078e9 | 2016-07-13 21:13:29 -0700 | [diff] [blame] | 46 | <class 'generator'> |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 47 | >>> list(g) |
| 48 | [0, 1, 4, 9] |
| 49 | |
| 50 | Test direct calls to next() |
| 51 | |
| 52 | >>> g = (i*i for i in range(3)) |
Georg Brandl | a18af4e | 2007-04-21 15:47:16 +0000 | [diff] [blame] | 53 | >>> next(g) |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 54 | 0 |
Georg Brandl | a18af4e | 2007-04-21 15:47:16 +0000 | [diff] [blame] | 55 | >>> next(g) |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 56 | 1 |
Georg Brandl | a18af4e | 2007-04-21 15:47:16 +0000 | [diff] [blame] | 57 | >>> next(g) |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 58 | 4 |
Georg Brandl | a18af4e | 2007-04-21 15:47:16 +0000 | [diff] [blame] | 59 | >>> next(g) |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 60 | Traceback (most recent call last): |
| 61 | File "<pyshell#21>", line 1, in -toplevel- |
Georg Brandl | a18af4e | 2007-04-21 15:47:16 +0000 | [diff] [blame] | 62 | next(g) |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 63 | StopIteration |
| 64 | |
| 65 | Does it stay stopped? |
| 66 | |
Georg Brandl | a18af4e | 2007-04-21 15:47:16 +0000 | [diff] [blame] | 67 | >>> next(g) |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 68 | Traceback (most recent call last): |
| 69 | File "<pyshell#21>", line 1, in -toplevel- |
Georg Brandl | a18af4e | 2007-04-21 15:47:16 +0000 | [diff] [blame] | 70 | next(g) |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 71 | StopIteration |
| 72 | >>> list(g) |
| 73 | [] |
| 74 | |
| 75 | Test running gen when defining function is out of scope |
| 76 | |
| 77 | >>> def f(n): |
Guido van Rossum | 805365e | 2007-05-07 22:24:25 +0000 | [diff] [blame] | 78 | ... return (i*i for i in range(n)) |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 79 | >>> list(f(10)) |
| 80 | [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] |
| 81 | |
| 82 | >>> def f(n): |
Guido van Rossum | 805365e | 2007-05-07 22:24:25 +0000 | [diff] [blame] | 83 | ... return ((i,j) for i in range(3) for j in range(n)) |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 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): |
Guido van Rossum | 805365e | 2007-05-07 22:24:25 +0000 | [diff] [blame] | 87 | ... return ((i,j) for i in range(3) for j in range(4) if j in range(n)) |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 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 | |
Raymond Hettinger | 3258e72 | 2004-08-16 01:35:28 +0000 | [diff] [blame] | 93 | Verify that parenthesis are required in a statement |
Raymond Hettinger | 84f107d | 2004-08-16 01:45:34 +0000 | [diff] [blame] | 94 | |
| 95 | >>> def f(n): |
Guido van Rossum | 805365e | 2007-05-07 22:24:25 +0000 | [diff] [blame] | 96 | ... return i*i for i in range(n) |
Raymond Hettinger | 84f107d | 2004-08-16 01:45:34 +0000 | [diff] [blame] | 97 | Traceback (most recent call last): |
| 98 | ... |
| 99 | SyntaxError: invalid syntax |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 100 | |
Neal Norwitz | 37c0844 | 2005-10-21 06:24:02 +0000 | [diff] [blame] | 101 | Verify that parenthesis are required when used as a keyword argument value |
| 102 | |
Guido van Rossum | 805365e | 2007-05-07 22:24:25 +0000 | [diff] [blame] | 103 | >>> dict(a = i for i in range(10)) |
Neal Norwitz | 37c0844 | 2005-10-21 06:24:02 +0000 | [diff] [blame] | 104 | Traceback (most recent call last): |
| 105 | ... |
| 106 | SyntaxError: invalid syntax |
| 107 | |
| 108 | Verify that parenthesis are required when used as a keyword argument value |
| 109 | |
Guido van Rossum | 805365e | 2007-05-07 22:24:25 +0000 | [diff] [blame] | 110 | >>> dict(a = (i for i in range(10))) #doctest: +ELLIPSIS |
Alexandre Vassalotti | bee3253 | 2008-05-16 18:15:12 +0000 | [diff] [blame] | 111 | {'a': <generator object <genexpr> at ...>} |
Neal Norwitz | 37c0844 | 2005-10-21 06:24:02 +0000 | [diff] [blame] | 112 | |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 113 | Verify 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 | |
Raymond Hettinger | 83ee795 | 2004-05-20 23:04:13 +0000 | [diff] [blame] | 121 | Verify that the outermost for-expression makes an immediate check |
| 122 | for 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) |
Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 128 | TypeError: 'int' object is not iterable |
Raymond Hettinger | 83ee795 | 2004-05-20 23:04:13 +0000 | [diff] [blame] | 129 | |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 130 | Verify 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 | |
| 138 | Verify 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 | |
| 145 | Verify re-use of tuples (a side benefit of using genexps over listcomps) |
| 146 | |
Guido van Rossum | c1f779c | 2007-07-03 08:25:58 +0000 | [diff] [blame] | 147 | >>> tupleids = list(map(id, ((i,i) for i in range(10)))) |
Thomas Wouters | 49fd7fa | 2006-04-21 10:40:58 +0000 | [diff] [blame] | 148 | >>> int(max(tupleids) - min(tupleids)) |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 149 | 0 |
| 150 | |
Raymond Hettinger | 7b46f6b | 2004-09-30 22:29:03 +0000 | [diff] [blame] | 151 | Verify 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 | ... |
Serhiy Storchaka | 97f1efb | 2018-11-20 19:27:16 +0200 | [diff] [blame] | 156 | SyntaxError: cannot assign to generator expression |
Raymond Hettinger | 7b46f6b | 2004-09-30 22:29:03 +0000 | [diff] [blame] | 157 | |
| 158 | >>> (y for y in (1,2)) += 10 |
| 159 | Traceback (most recent call last): |
| 160 | ... |
Serhiy Storchaka | 97f1efb | 2018-11-20 19:27:16 +0200 | [diff] [blame] | 161 | SyntaxError: cannot assign to generator expression |
Raymond Hettinger | 7b46f6b | 2004-09-30 22:29:03 +0000 | [diff] [blame] | 162 | |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 163 | |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 164 | ########### Tests borrowed from or inspired by test_generators.py ############ |
| 165 | |
| 166 | Make a generator that acts like range() |
| 167 | |
Guido van Rossum | 805365e | 2007-05-07 22:24:25 +0000 | [diff] [blame] | 168 | >>> yrange = lambda n: (i for i in range(n)) |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 169 | >>> list(yrange(10)) |
| 170 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] |
| 171 | |
| 172 | Generators always return to the most recent caller: |
| 173 | |
| 174 | >>> def creator(): |
| 175 | ... r = yrange(5) |
Georg Brandl | a18af4e | 2007-04-21 15:47:16 +0000 | [diff] [blame] | 176 | ... print("creator", next(r)) |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 177 | ... return r |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 178 | >>> def caller(): |
| 179 | ... r = creator() |
| 180 | ... for i in r: |
Guido van Rossum | 7131f84 | 2007-02-09 20:13:25 +0000 | [diff] [blame] | 181 | ... print("caller", i) |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 182 | >>> caller() |
| 183 | creator 0 |
| 184 | caller 1 |
| 185 | caller 2 |
| 186 | caller 3 |
| 187 | caller 4 |
| 188 | |
| 189 | Generators can call other generators: |
| 190 | |
| 191 | >>> def zrange(n): |
| 192 | ... for i in yrange(n): |
| 193 | ... yield i |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 194 | >>> list(zrange(5)) |
| 195 | [0, 1, 2, 3, 4] |
| 196 | |
| 197 | |
| 198 | Verify that a gen exp cannot be resumed while it is actively running: |
| 199 | |
Guido van Rossum | 805365e | 2007-05-07 22:24:25 +0000 | [diff] [blame] | 200 | >>> g = (next(me) for i in range(10)) |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 201 | >>> me = g |
Georg Brandl | a18af4e | 2007-04-21 15:47:16 +0000 | [diff] [blame] | 202 | >>> next(me) |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 203 | Traceback (most recent call last): |
| 204 | File "<pyshell#30>", line 1, in -toplevel- |
Georg Brandl | a18af4e | 2007-04-21 15:47:16 +0000 | [diff] [blame] | 205 | next(me) |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 206 | File "<pyshell#28>", line 1, in <generator expression> |
Guido van Rossum | 805365e | 2007-05-07 22:24:25 +0000 | [diff] [blame] | 207 | g = (next(me) for i in range(10)) |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 208 | ValueError: generator already executing |
| 209 | |
| 210 | Verify exception propagation |
| 211 | |
| 212 | >>> g = (10 // i for i in (5, 0, 2)) |
Georg Brandl | a18af4e | 2007-04-21 15:47:16 +0000 | [diff] [blame] | 213 | >>> next(g) |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 214 | 2 |
Georg Brandl | a18af4e | 2007-04-21 15:47:16 +0000 | [diff] [blame] | 215 | >>> next(g) |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 216 | Traceback (most recent call last): |
| 217 | File "<pyshell#37>", line 1, in -toplevel- |
Georg Brandl | a18af4e | 2007-04-21 15:47:16 +0000 | [diff] [blame] | 218 | next(g) |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 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 |
Georg Brandl | a18af4e | 2007-04-21 15:47:16 +0000 | [diff] [blame] | 222 | >>> next(g) |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 223 | Traceback (most recent call last): |
| 224 | File "<pyshell#38>", line 1, in -toplevel- |
Georg Brandl | a18af4e | 2007-04-21 15:47:16 +0000 | [diff] [blame] | 225 | next(g) |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 226 | StopIteration |
| 227 | |
| 228 | Make sure that None is a valid return value |
| 229 | |
Guido van Rossum | 805365e | 2007-05-07 22:24:25 +0000 | [diff] [blame] | 230 | >>> list(None for i in range(10)) |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 231 | [None, None, None, None, None, None, None, None, None, None] |
| 232 | |
| 233 | Check that generator attributes are present |
| 234 | |
| 235 | >>> g = (i*i for i in range(3)) |
Georg Brandl | a18af4e | 2007-04-21 15:47:16 +0000 | [diff] [blame] | 236 | >>> expected = set(['gi_frame', 'gi_running']) |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 237 | >>> set(attr for attr in dir(g) if not attr.startswith('__')) >= expected |
| 238 | True |
| 239 | |
Serhiy Storchaka | 9a11f17 | 2013-01-31 16:11:04 +0200 | [diff] [blame] | 240 | >>> from test.support import HAVE_DOCSTRINGS |
Larry Hastings | 581ee36 | 2014-01-28 05:00:08 -0800 | [diff] [blame] | 241 | >>> print(g.__next__.__doc__ if HAVE_DOCSTRINGS else 'Implement next(self).') |
| 242 | Implement next(self). |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 243 | >>> import types |
| 244 | >>> isinstance(g, types.GeneratorType) |
| 245 | True |
| 246 | |
| 247 | Check the __iter__ slot is defined to return self |
| 248 | |
| 249 | >>> iter(g) is g |
| 250 | True |
| 251 | |
| 252 | Verify 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 |
Georg Brandl | a18af4e | 2007-04-21 15:47:16 +0000 | [diff] [blame] | 258 | >>> next(me) |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 259 | 1 |
| 260 | >>> me.gi_running |
| 261 | 0 |
| 262 | |
| 263 | Verify 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 | |
Brett Cannon | 7a54073 | 2011-02-22 03:04:06 +0000 | [diff] [blame] | 277 | import sys |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 278 | |
Brett Cannon | 7a54073 | 2011-02-22 03:04:06 +0000 | [diff] [blame] | 279 | # Trace function can throw off the tuple reuse test. |
| 280 | if hasattr(sys, 'gettrace') and sys.gettrace(): |
| 281 | __test__ = {} |
| 282 | else: |
| 283 | __test__ = {'doctests' : doctests} |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 284 | |
| 285 | def test_main(verbose=None): |
Benjamin Peterson | ee8712c | 2008-05-20 21:35:26 +0000 | [diff] [blame] | 286 | from test import support |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 287 | from test import test_genexps |
Benjamin Peterson | ab078e9 | 2016-07-13 21:13:29 -0700 | [diff] [blame] | 288 | support.run_doctest(test_genexps, verbose) |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 289 | |
| 290 | # verify reference counting |
| 291 | if verbose and hasattr(sys, "gettotalrefcount"): |
| 292 | import gc |
| 293 | counts = [None] * 5 |
Guido van Rossum | 805365e | 2007-05-07 22:24:25 +0000 | [diff] [blame] | 294 | for i in range(len(counts)): |
Benjamin Peterson | ee8712c | 2008-05-20 21:35:26 +0000 | [diff] [blame] | 295 | support.run_doctest(test_genexps, verbose) |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 296 | gc.collect() |
| 297 | counts[i] = sys.gettotalrefcount() |
Guido van Rossum | be19ed7 | 2007-02-09 05:37:30 +0000 | [diff] [blame] | 298 | print(counts) |
Raymond Hettinger | 354433a | 2004-05-19 08:20:33 +0000 | [diff] [blame] | 299 | |
| 300 | if __name__ == "__main__": |
| 301 | test_main(verbose=True) |