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