diff options
author | Yury Selivanov <yselivanov@sprymix.com> | 2015-05-12 02:57:16 (GMT) |
---|---|---|
committer | Yury Selivanov <yselivanov@sprymix.com> | 2015-05-12 02:57:16 (GMT) |
commit | 7544508f0245173bff5866aa1598c8f6cce1fc5f (patch) | |
tree | bf80850d9cd46fc811f04b8c2484fb50775c697d /Lib/test | |
parent | 4e6bf4b3da03b132b0698f30ee931a350585b117 (diff) | |
download | cpython-7544508f0245173bff5866aa1598c8f6cce1fc5f.zip cpython-7544508f0245173bff5866aa1598c8f6cce1fc5f.tar.gz cpython-7544508f0245173bff5866aa1598c8f6cce1fc5f.tar.bz2 |
PEP 0492 -- Coroutines with async and await syntax. Issue #24017.
Diffstat (limited to 'Lib/test')
-rw-r--r-- | Lib/test/badsyntax_async1.py | 3 | ||||
-rw-r--r-- | Lib/test/badsyntax_async2.py | 3 | ||||
-rw-r--r-- | Lib/test/badsyntax_async3.py | 2 | ||||
-rw-r--r-- | Lib/test/badsyntax_async4.py | 2 | ||||
-rw-r--r-- | Lib/test/badsyntax_async5.py | 2 | ||||
-rw-r--r-- | Lib/test/badsyntax_async6.py | 2 | ||||
-rw-r--r-- | Lib/test/badsyntax_async7.py | 2 | ||||
-rw-r--r-- | Lib/test/badsyntax_async8.py | 2 | ||||
-rw-r--r-- | Lib/test/badsyntax_async9.py | 2 | ||||
-rw-r--r-- | Lib/test/exception_hierarchy.txt | 1 | ||||
-rw-r--r-- | Lib/test/test_ast.py | 9 | ||||
-rw-r--r-- | Lib/test/test_collections.py | 80 | ||||
-rw-r--r-- | Lib/test/test_coroutines.py | 862 | ||||
-rw-r--r-- | Lib/test/test_dis.py | 46 | ||||
-rw-r--r-- | Lib/test/test_grammar.py | 87 | ||||
-rw-r--r-- | Lib/test/test_inspect.py | 72 | ||||
-rw-r--r-- | Lib/test/test_minidom.py | 15 | ||||
-rw-r--r-- | Lib/test/test_parser.py | 16 | ||||
-rw-r--r-- | Lib/test/test_pickle.py | 4 | ||||
-rw-r--r-- | Lib/test/test_sys.py | 4 | ||||
-rw-r--r-- | Lib/test/test_tokenize.py | 186 | ||||
-rw-r--r-- | Lib/test/test_types.py | 35 |
22 files changed, 1416 insertions, 21 deletions
diff --git a/Lib/test/badsyntax_async1.py b/Lib/test/badsyntax_async1.py new file mode 100644 index 0000000..970445d --- /dev/null +++ b/Lib/test/badsyntax_async1.py @@ -0,0 +1,3 @@ +async def foo(): + def foo(a=await something()): + pass diff --git a/Lib/test/badsyntax_async2.py b/Lib/test/badsyntax_async2.py new file mode 100644 index 0000000..1e62a3e --- /dev/null +++ b/Lib/test/badsyntax_async2.py @@ -0,0 +1,3 @@ +async def foo(): + def foo(a:await something()): + pass diff --git a/Lib/test/badsyntax_async3.py b/Lib/test/badsyntax_async3.py new file mode 100644 index 0000000..dde1bc5 --- /dev/null +++ b/Lib/test/badsyntax_async3.py @@ -0,0 +1,2 @@ +async def foo(): + [i async for i in els] diff --git a/Lib/test/badsyntax_async4.py b/Lib/test/badsyntax_async4.py new file mode 100644 index 0000000..4afda40 --- /dev/null +++ b/Lib/test/badsyntax_async4.py @@ -0,0 +1,2 @@ +async def foo(): + async def foo(): await something() diff --git a/Lib/test/badsyntax_async5.py b/Lib/test/badsyntax_async5.py new file mode 100644 index 0000000..9d19af6 --- /dev/null +++ b/Lib/test/badsyntax_async5.py @@ -0,0 +1,2 @@ +def foo(): + await something() diff --git a/Lib/test/badsyntax_async6.py b/Lib/test/badsyntax_async6.py new file mode 100644 index 0000000..cb0a23d --- /dev/null +++ b/Lib/test/badsyntax_async6.py @@ -0,0 +1,2 @@ +async def foo(): + yield diff --git a/Lib/test/badsyntax_async7.py b/Lib/test/badsyntax_async7.py new file mode 100644 index 0000000..51e4bf9 --- /dev/null +++ b/Lib/test/badsyntax_async7.py @@ -0,0 +1,2 @@ +async def foo(): + yield from [] diff --git a/Lib/test/badsyntax_async8.py b/Lib/test/badsyntax_async8.py new file mode 100644 index 0000000..3c636f9 --- /dev/null +++ b/Lib/test/badsyntax_async8.py @@ -0,0 +1,2 @@ +async def foo(): + await await fut diff --git a/Lib/test/badsyntax_async9.py b/Lib/test/badsyntax_async9.py new file mode 100644 index 0000000..d033b28 --- /dev/null +++ b/Lib/test/badsyntax_async9.py @@ -0,0 +1,2 @@ +async def foo(): + await diff --git a/Lib/test/exception_hierarchy.txt b/Lib/test/exception_hierarchy.txt index 1c1f69f..6632826 100644 --- a/Lib/test/exception_hierarchy.txt +++ b/Lib/test/exception_hierarchy.txt @@ -4,6 +4,7 @@ BaseException +-- GeneratorExit +-- Exception +-- StopIteration + +-- StopAsyncIteration +-- ArithmeticError | +-- FloatingPointError | +-- OverflowError diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index 1d9de2c..c220a3c 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -106,6 +106,12 @@ exec_tests = [ "{r for l in x if g}", # setcomp with naked tuple "{r for l,m in x}", + # AsyncFunctionDef + "async def f():\n await something()", + # AsyncFor + "async def f():\n async for e in i: 1\n else: 2", + # AsyncWith + "async def f():\n async with a as b: 1", ] # These are compiled through "single" @@ -974,6 +980,9 @@ exec_results = [ ('Module', [('Expr', (1, 0), ('DictComp', (1, 1), ('Name', (1, 1), 'a', ('Load',)), ('Name', (1, 5), 'b', ('Load',)), [('comprehension', ('Tuple', (1, 11), [('Name', (1, 11), 'v', ('Store',)), ('Name', (1, 13), 'w', ('Store',))], ('Store',)), ('Name', (1, 18), 'x', ('Load',)), [])]))]), ('Module', [('Expr', (1, 0), ('SetComp', (1, 1), ('Name', (1, 1), 'r', ('Load',)), [('comprehension', ('Name', (1, 7), 'l', ('Store',)), ('Name', (1, 12), 'x', ('Load',)), [('Name', (1, 17), 'g', ('Load',))])]))]), ('Module', [('Expr', (1, 0), ('SetComp', (1, 1), ('Name', (1, 1), 'r', ('Load',)), [('comprehension', ('Tuple', (1, 7), [('Name', (1, 7), 'l', ('Store',)), ('Name', (1, 9), 'm', ('Store',))], ('Store',)), ('Name', (1, 14), 'x', ('Load',)), [])]))]), +('Module', [('AsyncFunctionDef', (1, 6), 'f', ('arguments', [], None, [], [], None, []), [('Expr', (2, 1), ('Await', (2, 1), ('Call', (2, 7), ('Name', (2, 7), 'something', ('Load',)), [], [])))], [], None)]), +('Module', [('AsyncFunctionDef', (1, 6), 'f', ('arguments', [], None, [], [], None, []), [('AsyncFor', (2, 7), ('Name', (2, 11), 'e', ('Store',)), ('Name', (2, 16), 'i', ('Load',)), [('Expr', (2, 19), ('Num', (2, 19), 1))], [('Expr', (3, 7), ('Num', (3, 7), 2))])], [], None)]), +('Module', [('AsyncFunctionDef', (1, 6), 'f', ('arguments', [], None, [], [], None, []), [('AsyncWith', (2, 7), [('withitem', ('Name', (2, 12), 'a', ('Load',)), ('Name', (2, 17), 'b', ('Store',)))], [('Expr', (2, 20), ('Num', (2, 20), 1))])], [], None)]), ] single_results = [ ('Interactive', [('Expr', (1, 0), ('BinOp', (1, 0), ('Num', (1, 0), 1), ('Add',), ('Num', (1, 2), 2)))]), diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index 5b2e81f..ad94fdd 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -11,9 +11,11 @@ from random import randrange, shuffle import keyword import re import sys +import types from collections import UserDict from collections import ChainMap from collections import deque +from collections.abc import Awaitable, Coroutine from collections.abc import Hashable, Iterable, Iterator, Generator from collections.abc import Sized, Container, Callable from collections.abc import Set, MutableSet @@ -446,6 +448,84 @@ class ABCTestCase(unittest.TestCase): class TestOneTrickPonyABCs(ABCTestCase): + def test_Awaitable(self): + def gen(): + yield + + @types.coroutine + def coro(): + yield + + async def new_coro(): + pass + + class Bar: + def __await__(self): + yield + + class MinimalCoro(Coroutine): + def send(self, value): + return value + def throw(self, typ, val=None, tb=None): + super().throw(typ, val, tb) + + non_samples = [None, int(), gen(), object()] + for x in non_samples: + self.assertNotIsInstance(x, Awaitable) + self.assertFalse(issubclass(type(x), Awaitable), repr(type(x))) + + samples = [Bar(), MinimalCoro()] + for x in samples: + self.assertIsInstance(x, Awaitable) + self.assertTrue(issubclass(type(x), Awaitable)) + + c = coro() + self.assertIsInstance(c, Awaitable) + c.close() # awoid RuntimeWarning that coro() was not awaited + + c = new_coro() + self.assertIsInstance(c, Awaitable) + c.close() # awoid RuntimeWarning that coro() was not awaited + + def test_Coroutine(self): + def gen(): + yield + + @types.coroutine + def coro(): + yield + + async def new_coro(): + pass + + class Bar: + def __await__(self): + yield + + class MinimalCoro(Coroutine): + def send(self, value): + return value + def throw(self, typ, val=None, tb=None): + super().throw(typ, val, tb) + + non_samples = [None, int(), gen(), object(), Bar()] + for x in non_samples: + self.assertNotIsInstance(x, Coroutine) + self.assertFalse(issubclass(type(x), Coroutine), repr(type(x))) + + samples = [MinimalCoro()] + for x in samples: + self.assertIsInstance(x, Awaitable) + self.assertTrue(issubclass(type(x), Awaitable)) + + c = coro() + self.assertIsInstance(c, Coroutine) + c.close() # awoid RuntimeWarning that coro() was not awaited + + c = new_coro() + self.assertIsInstance(c, Coroutine) + c.close() # awoid RuntimeWarning that coro() was not awaited + def test_Hashable(self): # Check some non-hashables non_samples = [bytearray(), list(), set(), dict()] diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py new file mode 100644 index 0000000..2a45225 --- /dev/null +++ b/Lib/test/test_coroutines.py @@ -0,0 +1,862 @@ +import contextlib +import gc +import sys +import types +import unittest +import warnings +from test import support + + +class AsyncYieldFrom: + def __init__(self, obj): + self.obj = obj + + def __await__(self): + yield from self.obj + + +class AsyncYield: + def __init__(self, value): + self.value = value + + def __await__(self): + yield self.value + + +def run_async(coro): + assert coro.__class__ is types.GeneratorType + + buffer = [] + result = None + while True: + try: + buffer.append(coro.send(None)) + except StopIteration as ex: + result = ex.args[0] if ex.args else None + break + return buffer, result + + +@contextlib.contextmanager +def silence_coro_gc(): + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + yield + support.gc_collect() + + +class AsyncBadSyntaxTest(unittest.TestCase): + + def test_badsyntax_1(self): + with self.assertRaisesRegex(SyntaxError, 'invalid syntax'): + import test.badsyntax_async1 + + def test_badsyntax_2(self): + with self.assertRaisesRegex(SyntaxError, 'invalid syntax'): + import test.badsyntax_async2 + + def test_badsyntax_3(self): + with self.assertRaisesRegex(SyntaxError, 'invalid syntax'): + import test.badsyntax_async3 + + def test_badsyntax_4(self): + with self.assertRaisesRegex(SyntaxError, 'invalid syntax'): + import test.badsyntax_async4 + + def test_badsyntax_5(self): + with self.assertRaisesRegex(SyntaxError, 'invalid syntax'): + import test.badsyntax_async5 + + def test_badsyntax_6(self): + with self.assertRaisesRegex( + SyntaxError, "'yield' inside async function"): + + import test.badsyntax_async6 + + def test_badsyntax_7(self): + with self.assertRaisesRegex( + SyntaxError, "'yield from' inside async function"): + + import test.badsyntax_async7 + + def test_badsyntax_8(self): + with self.assertRaisesRegex(SyntaxError, 'invalid syntax'): + import test.badsyntax_async8 + + def test_badsyntax_9(self): + with self.assertRaisesRegex(SyntaxError, 'invalid syntax'): + import test.badsyntax_async9 + + +class CoroutineTest(unittest.TestCase): + + def test_gen_1(self): + def gen(): yield + self.assertFalse(hasattr(gen, '__await__')) + + def test_func_1(self): + async def foo(): + return 10 + + f = foo() + self.assertIsInstance(f, types.GeneratorType) + self.assertTrue(bool(foo.__code__.co_flags & 0x80)) + self.assertTrue(bool(foo.__code__.co_flags & 0x20)) + self.assertTrue(bool(f.gi_code.co_flags & 0x80)) + self.assertTrue(bool(f.gi_code.co_flags & 0x20)) + self.assertEqual(run_async(f), ([], 10)) + + def bar(): pass + self.assertFalse(bool(bar.__code__.co_flags & 0x80)) + + def test_func_2(self): + async def foo(): + raise StopIteration + + with self.assertRaisesRegex( + RuntimeError, "generator raised StopIteration"): + + run_async(foo()) + + def test_func_3(self): + async def foo(): + raise StopIteration + + with silence_coro_gc(): + self.assertRegex(repr(foo()), '^<coroutine object.* at 0x.*>$') + + def test_func_4(self): + async def foo(): + raise StopIteration + + check = lambda: self.assertRaisesRegex( + TypeError, "coroutine-objects do not support iteration") + + with check(): + list(foo()) + + with check(): + tuple(foo()) + + with check(): + sum(foo()) + + with check(): + iter(foo()) + + with check(): + next(foo()) + + with silence_coro_gc(), check(): + for i in foo(): + pass + + with silence_coro_gc(), check(): + [i for i in foo()] + + def test_func_5(self): + @types.coroutine + def bar(): + yield 1 + + async def foo(): + await bar() + + check = lambda: self.assertRaisesRegex( + TypeError, "coroutine-objects do not support iteration") + + with check(): + for el in foo(): pass + + # the following should pass without an error + for el in bar(): + self.assertEqual(el, 1) + self.assertEqual([el for el in bar()], [1]) + self.assertEqual(tuple(bar()), (1,)) + self.assertEqual(next(iter(bar())), 1) + + def test_func_6(self): + @types.coroutine + def bar(): + yield 1 + yield 2 + + async def foo(): + await bar() + + f = foo() + self.assertEquals(f.send(None), 1) + self.assertEquals(f.send(None), 2) + with self.assertRaises(StopIteration): + f.send(None) + + def test_func_7(self): + async def bar(): + return 10 + + def foo(): + yield from bar() + + with silence_coro_gc(), self.assertRaisesRegex( + TypeError, + "cannot 'yield from' a coroutine object from a generator"): + + list(foo()) + + def test_func_8(self): + @types.coroutine + def bar(): + return (yield from foo()) + + async def foo(): + return 'spam' + + self.assertEqual(run_async(bar()), ([], 'spam') ) + + def test_func_9(self): + async def foo(): pass + + with self.assertWarnsRegex( + RuntimeWarning, "coroutine '.*test_func_9.*foo' was never awaited"): + + foo() + support.gc_collect() + + def test_await_1(self): + + async def foo(): + await 1 + with self.assertRaisesRegex(TypeError, "object int can.t.*await"): + run_async(foo()) + + def test_await_2(self): + async def foo(): + await [] + with self.assertRaisesRegex(TypeError, "object list can.t.*await"): + run_async(foo()) + + def test_await_3(self): + async def foo(): + await AsyncYieldFrom([1, 2, 3]) + + self.assertEqual(run_async(foo()), ([1, 2, 3], None)) + + def test_await_4(self): + async def bar(): + return 42 + + async def foo(): + return await bar() + + self.assertEqual(run_async(foo()), ([], 42)) + + def test_await_5(self): + class Awaitable: + def __await__(self): + return + + async def foo(): + return (await Awaitable()) + + with self.assertRaisesRegex( + TypeError, "__await__.*returned non-iterator of type"): + + run_async(foo()) + + def test_await_6(self): + class Awaitable: + def __await__(self): + return iter([52]) + + async def foo(): + return (await Awaitable()) + + self.assertEqual(run_async(foo()), ([52], None)) + + def test_await_7(self): + class Awaitable: + def __await__(self): + yield 42 + return 100 + + async def foo(): + return (await Awaitable()) + + self.assertEqual(run_async(foo()), ([42], 100)) + + def test_await_8(self): + class Awaitable: + pass + + async def foo(): + return (await Awaitable()) + + with self.assertRaisesRegex( + TypeError, "object Awaitable can't be used in 'await' expression"): + + run_async(foo()) + + def test_await_9(self): + def wrap(): + return bar + + async def bar(): + return 42 + + async def foo(): + b = bar() + + db = {'b': lambda: wrap} + + class DB: + b = wrap + + return (await bar() + await wrap()() + await db['b']()()() + + await bar() * 1000 + await DB.b()()) + + async def foo2(): + return -await bar() + + self.assertEqual(run_async(foo()), ([], 42168)) + self.assertEqual(run_async(foo2()), ([], -42)) + + def test_await_10(self): + async def baz(): + return 42 + + async def bar(): + return baz() + + async def foo(): + return await (await bar()) + + self.assertEqual(run_async(foo()), ([], 42)) + + def test_await_11(self): + def ident(val): + return val + + async def bar(): + return 'spam' + + async def foo(): + return ident(val=await bar()) + + async def foo2(): + return await bar(), 'ham' + + self.assertEqual(run_async(foo2()), ([], ('spam', 'ham'))) + + def test_await_12(self): + async def coro(): + return 'spam' + + class Awaitable: + def __await__(self): + return coro() + + async def foo(): + return await Awaitable() + + with self.assertRaisesRegex( + TypeError, "__await__\(\) returned a coroutine"): + + run_async(foo()) + + def test_await_13(self): + class Awaitable: + def __await__(self): + return self + + async def foo(): + return await Awaitable() + + with self.assertRaisesRegex( + TypeError, "__await__.*returned non-iterator of type"): + + run_async(foo()) + + def test_with_1(self): + class Manager: + def __init__(self, name): + self.name = name + + async def __aenter__(self): + await AsyncYieldFrom(['enter-1-' + self.name, + 'enter-2-' + self.name]) + return self + + async def __aexit__(self, *args): + await AsyncYieldFrom(['exit-1-' + self.name, + 'exit-2-' + self.name]) + + if self.name == 'B': + return True + + + async def foo(): + async with Manager("A") as a, Manager("B") as b: + await AsyncYieldFrom([('managers', a.name, b.name)]) + 1/0 + + f = foo() + result, _ = run_async(f) + + self.assertEqual( + result, ['enter-1-A', 'enter-2-A', 'enter-1-B', 'enter-2-B', + ('managers', 'A', 'B'), + 'exit-1-B', 'exit-2-B', 'exit-1-A', 'exit-2-A'] + ) + + async def foo(): + async with Manager("A") as a, Manager("C") as c: + await AsyncYieldFrom([('managers', a.name, c.name)]) + 1/0 + + with self.assertRaises(ZeroDivisionError): + run_async(foo()) + + def test_with_2(self): + class CM: + def __aenter__(self): + pass + + async def foo(): + async with CM(): + pass + + with self.assertRaisesRegex(AttributeError, '__aexit__'): + run_async(foo()) + + def test_with_3(self): + class CM: + def __aexit__(self): + pass + + async def foo(): + async with CM(): + pass + + with self.assertRaisesRegex(AttributeError, '__aenter__'): + run_async(foo()) + + def test_with_4(self): + class CM: + def __enter__(self): + pass + + def __exit__(self): + pass + + async def foo(): + async with CM(): + pass + + with self.assertRaisesRegex(AttributeError, '__aexit__'): + run_async(foo()) + + def test_with_5(self): + # While this test doesn't make a lot of sense, + # it's a regression test for an early bug with opcodes + # generation + + class CM: + async def __aenter__(self): + return self + + async def __aexit__(self, *exc): + pass + + async def func(): + async with CM(): + assert (1, ) == 1 + + with self.assertRaises(AssertionError): + run_async(func()) + + def test_with_6(self): + class CM: + def __aenter__(self): + return 123 + + def __aexit__(self, *e): + return 456 + + async def foo(): + async with CM(): + pass + + with self.assertRaisesRegex( + TypeError, "object int can't be used in 'await' expression"): + # it's important that __aexit__ wasn't called + run_async(foo()) + + def test_with_7(self): + class CM: + async def __aenter__(self): + return self + + def __aexit__(self, *e): + return 456 + + async def foo(): + async with CM(): + pass + + with self.assertRaisesRegex( + TypeError, "object int can't be used in 'await' expression"): + + run_async(foo()) + + def test_for_1(self): + aiter_calls = 0 + + class AsyncIter: + def __init__(self): + self.i = 0 + + async def __aiter__(self): + nonlocal aiter_calls + aiter_calls += 1 + return self + + async def __anext__(self): + self.i += 1 + + if not (self.i % 10): + await AsyncYield(self.i * 10) + + if self.i > 100: + raise StopAsyncIteration + + return self.i, self.i + + + buffer = [] + async def test1(): + async for i1, i2 in AsyncIter(): + buffer.append(i1 + i2) + + yielded, _ = run_async(test1()) + # Make sure that __aiter__ was called only once + self.assertEqual(aiter_calls, 1) + self.assertEqual(yielded, [i * 100 for i in range(1, 11)]) + self.assertEqual(buffer, [i*2 for i in range(1, 101)]) + + + buffer = [] + async def test2(): + nonlocal buffer + async for i in AsyncIter(): + buffer.append(i[0]) + if i[0] == 20: + break + else: + buffer.append('what?') + buffer.append('end') + + yielded, _ = run_async(test2()) + # Make sure that __aiter__ was called only once + self.assertEqual(aiter_calls, 2) + self.assertEqual(yielded, [100, 200]) + self.assertEqual(buffer, [i for i in range(1, 21)] + ['end']) + + + buffer = [] + async def test3(): + nonlocal buffer + async for i in AsyncIter(): + if i[0] > 20: + continue + buffer.append(i[0]) + else: + buffer.append('what?') + buffer.append('end') + + yielded, _ = run_async(test3()) + # Make sure that __aiter__ was called only once + self.assertEqual(aiter_calls, 3) + self.assertEqual(yielded, [i * 100 for i in range(1, 11)]) + self.assertEqual(buffer, [i for i in range(1, 21)] + + ['what?', 'end']) + + def test_for_2(self): + tup = (1, 2, 3) + refs_before = sys.getrefcount(tup) + + async def foo(): + async for i in tup: + print('never going to happen') + + with self.assertRaisesRegex( + TypeError, "async for' requires an object.*__aiter__.*tuple"): + + run_async(foo()) + + self.assertEqual(sys.getrefcount(tup), refs_before) + + def test_for_3(self): + class I: + def __aiter__(self): + return self + + aiter = I() + refs_before = sys.getrefcount(aiter) + + async def foo(): + async for i in aiter: + print('never going to happen') + + with self.assertRaisesRegex( + TypeError, + "async for' received an invalid object.*__aiter.*\: I"): + + run_async(foo()) + + self.assertEqual(sys.getrefcount(aiter), refs_before) + + def test_for_4(self): + class I: + async def __aiter__(self): + return self + + def __anext__(self): + return () + + aiter = I() + refs_before = sys.getrefcount(aiter) + + async def foo(): + async for i in aiter: + print('never going to happen') + + with self.assertRaisesRegex( + TypeError, + "async for' received an invalid object.*__anext__.*tuple"): + + run_async(foo()) + + self.assertEqual(sys.getrefcount(aiter), refs_before) + + def test_for_5(self): + class I: + async def __aiter__(self): + return self + + def __anext__(self): + return 123 + + async def foo(): + async for i in I(): + print('never going to happen') + + with self.assertRaisesRegex( + TypeError, + "async for' received an invalid object.*__anext.*int"): + + run_async(foo()) + + def test_for_6(self): + I = 0 + + class Manager: + async def __aenter__(self): + nonlocal I + I += 10000 + + async def __aexit__(self, *args): + nonlocal I + I += 100000 + + class Iterable: + def __init__(self): + self.i = 0 + + async def __aiter__(self): + return self + + async def __anext__(self): + if self.i > 10: + raise StopAsyncIteration + self.i += 1 + return self.i + + ############## + + manager = Manager() + iterable = Iterable() + mrefs_before = sys.getrefcount(manager) + irefs_before = sys.getrefcount(iterable) + + async def main(): + nonlocal I + + async with manager: + async for i in iterable: + I += 1 + I += 1000 + + run_async(main()) + self.assertEqual(I, 111011) + + self.assertEqual(sys.getrefcount(manager), mrefs_before) + self.assertEqual(sys.getrefcount(iterable), irefs_before) + + ############## + + async def main(): + nonlocal I + + async with Manager(): + async for i in Iterable(): + I += 1 + I += 1000 + + async with Manager(): + async for i in Iterable(): + I += 1 + I += 1000 + + run_async(main()) + self.assertEqual(I, 333033) + + ############## + + async def main(): + nonlocal I + + async with Manager(): + I += 100 + async for i in Iterable(): + I += 1 + else: + I += 10000000 + I += 1000 + + async with Manager(): + I += 100 + async for i in Iterable(): + I += 1 + else: + I += 10000000 + I += 1000 + + run_async(main()) + self.assertEqual(I, 20555255) + + +class CoroAsyncIOCompatTest(unittest.TestCase): + + def test_asyncio_1(self): + import asyncio + + class MyException(Exception): + pass + + buffer = [] + + class CM: + async def __aenter__(self): + buffer.append(1) + await asyncio.sleep(0.01) + buffer.append(2) + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + await asyncio.sleep(0.01) + buffer.append(exc_type.__name__) + + async def f(): + async with CM() as c: + await asyncio.sleep(0.01) + raise MyException + buffer.append('unreachable') + + loop = asyncio.get_event_loop() + try: + loop.run_until_complete(f()) + except MyException: + pass + finally: + loop.close() + + self.assertEqual(buffer, [1, 2, 'MyException']) + + +class SysSetCoroWrapperTest(unittest.TestCase): + + def test_set_wrapper_1(self): + async def foo(): + return 'spam' + + wrapped = None + def wrap(gen): + nonlocal wrapped + wrapped = gen + return gen + + self.assertIsNone(sys.get_coroutine_wrapper()) + + sys.set_coroutine_wrapper(wrap) + self.assertIs(sys.get_coroutine_wrapper(), wrap) + try: + f = foo() + self.assertTrue(wrapped) + + self.assertEqual(run_async(f), ([], 'spam')) + finally: + sys.set_coroutine_wrapper(None) + + self.assertIsNone(sys.get_coroutine_wrapper()) + + wrapped = None + with silence_coro_gc(): + foo() + self.assertFalse(wrapped) + + def test_set_wrapper_2(self): + self.assertIsNone(sys.get_coroutine_wrapper()) + with self.assertRaisesRegex(TypeError, "callable expected, got int"): + sys.set_coroutine_wrapper(1) + self.assertIsNone(sys.get_coroutine_wrapper()) + + +class CAPITest(unittest.TestCase): + + def test_tp_await_1(self): + from _testcapi import awaitType as at + + async def foo(): + future = at(iter([1])) + return (await future) + + self.assertEqual(foo().send(None), 1) + + def test_tp_await_2(self): + # Test tp_await to __await__ mapping + from _testcapi import awaitType as at + future = at(iter([1])) + self.assertEqual(next(future.__await__()), 1) + + def test_tp_await_3(self): + from _testcapi import awaitType as at + + async def foo(): + future = at(1) + return (await future) + + with self.assertRaisesRegex( + TypeError, "__await__.*returned non-iterator of type 'int'"): + self.assertEqual(foo().send(None), 1) + + +def test_main(): + support.run_unittest(AsyncBadSyntaxTest, + CoroutineTest, + CoroAsyncIOCompatTest, + SysSetCoroWrapperTest, + CAPITest) + + +if __name__=="__main__": + test_main() diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 7890b1f..421bbad 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -480,6 +480,24 @@ Constants: Names: 0: x""" + +async def async_def(): + await 1 + async for a in b: pass + async with c as d: pass + +code_info_async_def = """\ +Name: async_def +Filename: (.*) +Argument count: 0 +Kw-only arguments: 0 +Number of locals: 2 +Stack size: 17 +Flags: OPTIMIZED, NEWLOCALS, GENERATOR, NOFREE, COROUTINE +Constants: + 0: None + 1: 1""" + class CodeInfoTests(unittest.TestCase): test_pairs = [ (dis.code_info, code_info_code_info), @@ -488,6 +506,7 @@ class CodeInfoTests(unittest.TestCase): (expr_str, code_info_expr_str), (simple_stmt_str, code_info_simple_stmt_str), (compound_stmt_str, code_info_compound_stmt_str), + (async_def, code_info_async_def) ] def test_code_info(self): @@ -697,7 +716,7 @@ expected_opinfo_jumpy = [ Instruction(opname='LOAD_CONST', opcode=100, arg=6, argval='Who let lolcatz into this test suite?', argrepr="'Who let lolcatz into this test suite?'", offset=135, starts_line=None, is_jump_target=False), Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=138, starts_line=None, is_jump_target=False), Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=141, starts_line=None, is_jump_target=False), - Instruction(opname='SETUP_FINALLY', opcode=122, arg=72, argval=217, argrepr='to 217', offset=142, starts_line=20, is_jump_target=True), + Instruction(opname='SETUP_FINALLY', opcode=122, arg=73, argval=218, argrepr='to 218', offset=142, starts_line=20, is_jump_target=True), Instruction(opname='SETUP_EXCEPT', opcode=121, arg=12, argval=160, argrepr='to 160', offset=145, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval=1, argrepr='1', offset=148, starts_line=21, is_jump_target=False), Instruction(opname='LOAD_CONST', opcode=100, arg=7, argval=0, argrepr='0', offset=151, starts_line=None, is_jump_target=False), @@ -717,7 +736,7 @@ expected_opinfo_jumpy = [ Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=179, starts_line=None, is_jump_target=False), Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=182, starts_line=None, is_jump_target=False), Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=183, starts_line=None, is_jump_target=False), - Instruction(opname='JUMP_FORWARD', opcode=110, arg=26, argval=213, argrepr='to 213', offset=184, starts_line=None, is_jump_target=False), + Instruction(opname='JUMP_FORWARD', opcode=110, arg=27, argval=214, argrepr='to 214', offset=184, starts_line=None, is_jump_target=False), Instruction(opname='END_FINALLY', opcode=88, arg=None, argval=None, argrepr='', offset=187, starts_line=None, is_jump_target=True), Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=188, starts_line=25, is_jump_target=True), Instruction(opname='SETUP_WITH', opcode=143, arg=17, argval=211, argrepr='to 211', offset=191, starts_line=None, is_jump_target=False), @@ -728,17 +747,18 @@ expected_opinfo_jumpy = [ Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=206, starts_line=None, is_jump_target=False), Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=207, starts_line=None, is_jump_target=False), Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=208, starts_line=None, is_jump_target=False), - Instruction(opname='WITH_CLEANUP', opcode=81, arg=None, argval=None, argrepr='', offset=211, starts_line=None, is_jump_target=True), - Instruction(opname='END_FINALLY', opcode=88, arg=None, argval=None, argrepr='', offset=212, starts_line=None, is_jump_target=False), - Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=213, starts_line=None, is_jump_target=True), - Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=214, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=217, starts_line=28, is_jump_target=True), - Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=220, starts_line=None, is_jump_target=False), - Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=223, starts_line=None, is_jump_target=False), - Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=226, starts_line=None, is_jump_target=False), - Instruction(opname='END_FINALLY', opcode=88, arg=None, argval=None, argrepr='', offset=227, starts_line=None, is_jump_target=False), - Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=228, starts_line=None, is_jump_target=False), - Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=231, starts_line=None, is_jump_target=False), + Instruction(opname='WITH_CLEANUP_START', opcode=81, arg=None, argval=None, argrepr='', offset=211, starts_line=None, is_jump_target=True), + Instruction(opname='WITH_CLEANUP_FINISH', opcode=82, arg=None, argval=None, argrepr='', offset=212, starts_line=None, is_jump_target=False), + Instruction(opname='END_FINALLY', opcode=88, arg=None, argval=None, argrepr='', offset=213, starts_line=None, is_jump_target=False), + Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=214, starts_line=None, is_jump_target=True), + Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=215, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=218, starts_line=28, is_jump_target=True), + Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=221, starts_line=None, is_jump_target=False), + Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=224, starts_line=None, is_jump_target=False), + Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=227, starts_line=None, is_jump_target=False), + Instruction(opname='END_FINALLY', opcode=88, arg=None, argval=None, argrepr='', offset=228, starts_line=None, is_jump_target=False), + Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=229, starts_line=None, is_jump_target=False), + Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=232, starts_line=None, is_jump_target=False), ] # One last piece of inspect fodder to check the default line number handling diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py index 28b1f04..e46a232 100644 --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -2,6 +2,7 @@ # This just tests whether the parser accepts them all. from test.support import check_syntax_error +import inspect import unittest import sys # testing import * @@ -1034,6 +1035,92 @@ class GrammarTests(unittest.TestCase): m @= 42 self.assertEqual(m.other, 42) + def test_async_await(self): + async = 1 + await = 2 + self.assertEqual(async, 1) + + def async(): + nonlocal await + await = 10 + async() + self.assertEqual(await, 10) + + self.assertFalse(bool(async.__code__.co_flags & inspect.CO_COROUTINE)) + + async def test(): + def sum(): + async = 1 + await = 41 + return async + await + + if 1: + await someobj() + + self.assertEqual(test.__name__, 'test') + self.assertTrue(bool(test.__code__.co_flags & inspect.CO_COROUTINE)) + + def decorator(func): + setattr(func, '_marked', True) + return func + + @decorator + async def test2(): + return 22 + self.assertTrue(test2._marked) + self.assertEqual(test2.__name__, 'test2') + self.assertTrue(bool(test2.__code__.co_flags & inspect.CO_COROUTINE)) + + def test_async_for(self): + class Done(Exception): pass + + class AIter: + async def __aiter__(self): + return self + async def __anext__(self): + raise StopAsyncIteration + + async def foo(): + async for i in AIter(): + pass + async for i, j in AIter(): + pass + async for i in AIter(): + pass + else: + pass + raise Done + + with self.assertRaises(Done): + foo().send(None) + + def test_async_with(self): + class Done(Exception): pass + + class manager: + async def __aenter__(self): + return (1, 2) + async def __aexit__(self, *exc): + return False + + async def foo(): + async with manager(): + pass + async with manager() as x: + pass + async with manager() as (x, y): + pass + async with manager(), manager(): + pass + async with manager() as x, manager() as y: + pass + async with manager() as x, manager(): + pass + raise Done + + with self.assertRaises(Done): + foo().send(None) + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index 43ef755..20df755 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -18,6 +18,7 @@ import textwrap import unicodedata import unittest import unittest.mock +import warnings try: from concurrent.futures import ThreadPoolExecutor @@ -62,14 +63,16 @@ class IsTestBase(unittest.TestCase): predicates = set([inspect.isbuiltin, inspect.isclass, inspect.iscode, inspect.isframe, inspect.isfunction, inspect.ismethod, inspect.ismodule, inspect.istraceback, - inspect.isgenerator, inspect.isgeneratorfunction]) + inspect.isgenerator, inspect.isgeneratorfunction, + inspect.iscoroutine, inspect.iscoroutinefunction]) def istest(self, predicate, exp): obj = eval(exp) self.assertTrue(predicate(obj), '%s(%s)' % (predicate.__name__, exp)) for other in self.predicates - set([predicate]): - if predicate == inspect.isgeneratorfunction and\ + if (predicate == inspect.isgeneratorfunction or \ + predicate == inspect.iscoroutinefunction) and \ other == inspect.isfunction: continue self.assertFalse(other(obj), 'not %s(%s)' % (other.__name__, exp)) @@ -78,13 +81,21 @@ def generator_function_example(self): for i in range(2): yield i +async def coroutine_function_example(self): + return 'spam' + +@types.coroutine +def gen_coroutine_function_example(self): + yield + return 'spam' + class TestPredicates(IsTestBase): - def test_sixteen(self): + def test_eightteen(self): count = len([x for x in dir(inspect) if x.startswith('is')]) # This test is here for remember you to update Doc/library/inspect.rst # which claims there are 16 such functions - expected = 16 + expected = 19 err_msg = "There are %d (not %d) is* functions" % (count, expected) self.assertEqual(count, expected, err_msg) @@ -115,11 +126,64 @@ class TestPredicates(IsTestBase): self.istest(inspect.isdatadescriptor, 'collections.defaultdict.default_factory') self.istest(inspect.isgenerator, '(x for x in range(2))') self.istest(inspect.isgeneratorfunction, 'generator_function_example') + + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + self.istest(inspect.iscoroutine, 'coroutine_function_example(1)') + self.istest(inspect.iscoroutinefunction, 'coroutine_function_example') + if hasattr(types, 'MemberDescriptorType'): self.istest(inspect.ismemberdescriptor, 'datetime.timedelta.days') else: self.assertFalse(inspect.ismemberdescriptor(datetime.timedelta.days)) + def test_iscoroutine(self): + gen_coro = gen_coroutine_function_example(1) + coro = coroutine_function_example(1) + + self.assertTrue( + inspect.iscoroutinefunction(gen_coroutine_function_example)) + self.assertTrue(inspect.iscoroutine(gen_coro)) + + self.assertTrue( + inspect.isgeneratorfunction(gen_coroutine_function_example)) + self.assertTrue(inspect.isgenerator(gen_coro)) + + self.assertTrue( + inspect.iscoroutinefunction(coroutine_function_example)) + self.assertTrue(inspect.iscoroutine(coro)) + + self.assertFalse( + inspect.isgeneratorfunction(coroutine_function_example)) + self.assertFalse(inspect.isgenerator(coro)) + + coro.close(); gen_coro.close() # silence warnings + + def test_isawaitable(self): + def gen(): yield + self.assertFalse(inspect.isawaitable(gen())) + + coro = coroutine_function_example(1) + gen_coro = gen_coroutine_function_example(1) + + self.assertTrue( + inspect.isawaitable(coro)) + self.assertTrue( + inspect.isawaitable(gen_coro)) + + class Future: + def __await__(): + pass + self.assertTrue(inspect.isawaitable(Future())) + self.assertFalse(inspect.isawaitable(Future)) + + class NotFuture: pass + not_fut = NotFuture() + not_fut.__await__ = lambda: None + self.assertFalse(inspect.isawaitable(not_fut)) + + coro.close(); gen_coro.close() # silence warnings + def test_isroutine(self): self.assertTrue(inspect.isroutine(mod.spam)) self.assertTrue(inspect.isroutine([].count)) diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py index 686638c..e8ca497 100644 --- a/Lib/test/test_minidom.py +++ b/Lib/test/test_minidom.py @@ -49,6 +49,21 @@ class MinidomTest(unittest.TestCase): t = node.wholeText self.confirm(t == s, "looking for %r, found %r" % (s, t)) + def testDocumentAsyncAttr(self): + doc = Document() + self.assertFalse(doc.async_) + with self.assertWarns(DeprecationWarning): + self.assertFalse(getattr(doc, 'async', True)) + with self.assertWarns(DeprecationWarning): + setattr(doc, 'async', True) + with self.assertWarns(DeprecationWarning): + self.assertTrue(getattr(doc, 'async', False)) + self.assertTrue(doc.async_) + + self.assertFalse(Document.async_) + with self.assertWarns(DeprecationWarning): + self.assertFalse(getattr(Document, 'async', True)) + def testParseFromBinaryFile(self): with open(tstfile, 'rb') as file: dom = parse(file) diff --git a/Lib/test/test_parser.py b/Lib/test/test_parser.py index 56b5d95..7082273 100644 --- a/Lib/test/test_parser.py +++ b/Lib/test/test_parser.py @@ -63,6 +63,22 @@ class RoundtripLegalSyntaxTestCase(unittest.TestCase): " if (yield):\n" " yield x\n") + def test_await_statement(self): + self.check_suite("async def f():\n await smth()") + self.check_suite("async def f():\n foo = await smth()") + self.check_suite("async def f():\n foo, bar = await smth()") + self.check_suite("async def f():\n (await smth())") + self.check_suite("async def f():\n foo((await smth()))") + self.check_suite("async def f():\n await foo(); return 42") + + def test_async_with_statement(self): + self.check_suite("async def f():\n async with 1: pass") + self.check_suite("async def f():\n async with a as b, c as d: pass") + + def test_async_for_statement(self): + self.check_suite("async def f():\n async for i in (): pass") + self.check_suite("async def f():\n async for i, b in (): pass") + def test_nonlocal_statement(self): self.check_suite("def f():\n" " x = 0\n" diff --git a/Lib/test/test_pickle.py b/Lib/test/test_pickle.py index f5add27..a9853c1 100644 --- a/Lib/test/test_pickle.py +++ b/Lib/test/test_pickle.py @@ -351,7 +351,9 @@ class CompatPickleTests(unittest.TestCase): for name, exc in get_exceptions(builtins): with self.subTest(name): - if exc in (BlockingIOError, ResourceWarning): + if exc in (BlockingIOError, + ResourceWarning, + StopAsyncIteration): continue if exc is not OSError and issubclass(exc, OSError): self.assertEqual(reverse_mapping('builtins', name), diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 6d2763b..494a53b 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1025,9 +1025,9 @@ class SizeofTest(unittest.TestCase): # static type: PyTypeObject s = vsize('P2n15Pl4Pn9Pn11PIP') check(int, s) - # (PyTypeObject + PyNumberMethods + PyMappingMethods + + # (PyTypeObject + PyAsyncMethods + PyNumberMethods + PyMappingMethods + # PySequenceMethods + PyBufferProcs + 4P) - s = vsize('P2n17Pl4Pn9Pn11PIP') + struct.calcsize('34P 3P 10P 2P 4P') + s = vsize('P2n17Pl4Pn9Pn11PIP') + struct.calcsize('34P 3P 3P 10P 2P 4P') # Separate block for PyDictKeysObject with 4 entries s += struct.calcsize("2nPn") + 4*struct.calcsize("n2P") # class diff --git a/Lib/test/test_tokenize.py b/Lib/test/test_tokenize.py index 03f6148..ed75171 100644 --- a/Lib/test/test_tokenize.py +++ b/Lib/test/test_tokenize.py @@ -641,6 +641,192 @@ Legacy unicode literals: NAME 'grĂ¼n' (2, 0) (2, 4) OP '=' (2, 5) (2, 6) STRING "U'green'" (2, 7) (2, 15) + +Async/await extension: + + >>> dump_tokens("async = 1") + ENCODING 'utf-8' (0, 0) (0, 0) + NAME 'async' (1, 0) (1, 5) + OP '=' (1, 6) (1, 7) + NUMBER '1' (1, 8) (1, 9) + + >>> dump_tokens("a = (async = 1)") + ENCODING 'utf-8' (0, 0) (0, 0) + NAME 'a' (1, 0) (1, 1) + OP '=' (1, 2) (1, 3) + OP '(' (1, 4) (1, 5) + NAME 'async' (1, 5) (1, 10) + OP '=' (1, 11) (1, 12) + NUMBER '1' (1, 13) (1, 14) + OP ')' (1, 14) (1, 15) + + >>> dump_tokens("async()") + ENCODING 'utf-8' (0, 0) (0, 0) + NAME 'async' (1, 0) (1, 5) + OP '(' (1, 5) (1, 6) + OP ')' (1, 6) (1, 7) + + >>> dump_tokens("class async(Bar):pass") + ENCODING 'utf-8' (0, 0) (0, 0) + NAME 'class' (1, 0) (1, 5) + NAME 'async' (1, 6) (1, 11) + OP '(' (1, 11) (1, 12) + NAME 'Bar' (1, 12) (1, 15) + OP ')' (1, 15) (1, 16) + OP ':' (1, 16) (1, 17) + NAME 'pass' (1, 17) (1, 21) + + >>> dump_tokens("class async:pass") + ENCODING 'utf-8' (0, 0) (0, 0) + NAME 'class' (1, 0) (1, 5) + NAME 'async' (1, 6) (1, 11) + OP ':' (1, 11) (1, 12) + NAME 'pass' (1, 12) (1, 16) + + >>> dump_tokens("await = 1") + ENCODING 'utf-8' (0, 0) (0, 0) + NAME 'await' (1, 0) (1, 5) + OP '=' (1, 6) (1, 7) + NUMBER '1' (1, 8) (1, 9) + + >>> dump_tokens("foo.async") + ENCODING 'utf-8' (0, 0) (0, 0) + NAME 'foo' (1, 0) (1, 3) + OP '.' (1, 3) (1, 4) + NAME 'async' (1, 4) (1, 9) + + >>> dump_tokens("async for a in b: pass") + ENCODING 'utf-8' (0, 0) (0, 0) + NAME 'async' (1, 0) (1, 5) + NAME 'for' (1, 6) (1, 9) + NAME 'a' (1, 10) (1, 11) + NAME 'in' (1, 12) (1, 14) + NAME 'b' (1, 15) (1, 16) + OP ':' (1, 16) (1, 17) + NAME 'pass' (1, 18) (1, 22) + + >>> dump_tokens("async with a as b: pass") + ENCODING 'utf-8' (0, 0) (0, 0) + NAME 'async' (1, 0) (1, 5) + NAME 'with' (1, 6) (1, 10) + NAME 'a' (1, 11) (1, 12) + NAME 'as' (1, 13) (1, 15) + NAME 'b' (1, 16) (1, 17) + OP ':' (1, 17) (1, 18) + NAME 'pass' (1, 19) (1, 23) + + >>> dump_tokens("async.foo") + ENCODING 'utf-8' (0, 0) (0, 0) + NAME 'async' (1, 0) (1, 5) + OP '.' (1, 5) (1, 6) + NAME 'foo' (1, 6) (1, 9) + + >>> dump_tokens("async") + ENCODING 'utf-8' (0, 0) (0, 0) + NAME 'async' (1, 0) (1, 5) + + >>> dump_tokens("async\\n#comment\\nawait") + ENCODING 'utf-8' (0, 0) (0, 0) + NAME 'async' (1, 0) (1, 5) + NEWLINE '\\n' (1, 5) (1, 6) + COMMENT '#comment' (2, 0) (2, 8) + NL '\\n' (2, 8) (2, 9) + NAME 'await' (3, 0) (3, 5) + + >>> dump_tokens("async\\n...\\nawait") + ENCODING 'utf-8' (0, 0) (0, 0) + NAME 'async' (1, 0) (1, 5) + NEWLINE '\\n' (1, 5) (1, 6) + OP '...' (2, 0) (2, 3) + NEWLINE '\\n' (2, 3) (2, 4) + NAME 'await' (3, 0) (3, 5) + + >>> dump_tokens("async\\nawait") + ENCODING 'utf-8' (0, 0) (0, 0) + NAME 'async' (1, 0) (1, 5) + NEWLINE '\\n' (1, 5) (1, 6) + NAME 'await' (2, 0) (2, 5) + + >>> dump_tokens("foo.async + 1") + ENCODING 'utf-8' (0, 0) (0, 0) + NAME 'foo' (1, 0) (1, 3) + OP '.' (1, 3) (1, 4) + NAME 'async' (1, 4) (1, 9) + OP '+' (1, 10) (1, 11) + NUMBER '1' (1, 12) (1, 13) + + >>> dump_tokens("async def foo(): pass") + ENCODING 'utf-8' (0, 0) (0, 0) + ASYNC 'async' (1, 0) (1, 5) + NAME 'def' (1, 6) (1, 9) + NAME 'foo' (1, 10) (1, 13) + OP '(' (1, 13) (1, 14) + OP ')' (1, 14) (1, 15) + OP ':' (1, 15) (1, 16) + NAME 'pass' (1, 17) (1, 21) + + >>> dump_tokens('''async def foo(): + ... def foo(await): + ... await = 1 + ... if 1: + ... await + ... async += 1 + ... ''') + ENCODING 'utf-8' (0, 0) (0, 0) + ASYNC 'async' (1, 0) (1, 5) + NAME 'def' (1, 6) (1, 9) + NAME 'foo' (1, 10) (1, 13) + OP '(' (1, 13) (1, 14) + OP ')' (1, 14) (1, 15) + OP ':' (1, 15) (1, 16) + NEWLINE '\\n' (1, 16) (1, 17) + INDENT ' ' (2, 0) (2, 2) + NAME 'def' (2, 2) (2, 5) + NAME 'foo' (2, 6) (2, 9) + OP '(' (2, 9) (2, 10) + NAME 'await' (2, 10) (2, 15) + OP ')' (2, 15) (2, 16) + OP ':' (2, 16) (2, 17) + NEWLINE '\\n' (2, 17) (2, 18) + INDENT ' ' (3, 0) (3, 4) + NAME 'await' (3, 4) (3, 9) + OP '=' (3, 10) (3, 11) + NUMBER '1' (3, 12) (3, 13) + NEWLINE '\\n' (3, 13) (3, 14) + DEDENT '' (4, 2) (4, 2) + NAME 'if' (4, 2) (4, 4) + NUMBER '1' (4, 5) (4, 6) + OP ':' (4, 6) (4, 7) + NEWLINE '\\n' (4, 7) (4, 8) + INDENT ' ' (5, 0) (5, 4) + AWAIT 'await' (5, 4) (5, 9) + NEWLINE '\\n' (5, 9) (5, 10) + DEDENT '' (6, 0) (6, 0) + DEDENT '' (6, 0) (6, 0) + NAME 'async' (6, 0) (6, 5) + OP '+=' (6, 6) (6, 8) + NUMBER '1' (6, 9) (6, 10) + NEWLINE '\\n' (6, 10) (6, 11) + + >>> dump_tokens('''async def foo(): + ... async for i in 1: pass''') + ENCODING 'utf-8' (0, 0) (0, 0) + ASYNC 'async' (1, 0) (1, 5) + NAME 'def' (1, 6) (1, 9) + NAME 'foo' (1, 10) (1, 13) + OP '(' (1, 13) (1, 14) + OP ')' (1, 14) (1, 15) + OP ':' (1, 15) (1, 16) + NEWLINE '\\n' (1, 16) (1, 17) + INDENT ' ' (2, 0) (2, 2) + ASYNC 'async' (2, 2) (2, 7) + NAME 'for' (2, 8) (2, 11) + NAME 'i' (2, 12) (2, 13) + NAME 'in' (2, 14) (2, 16) + NUMBER '1' (2, 17) (2, 18) + OP ':' (2, 18) (2, 19) + NAME 'pass' (2, 20) (2, 24) + DEDENT '' (3, 0) (3, 0) """ from test import support diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index 8cdecb0..c5a35f9 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -1,7 +1,8 @@ # Python test set -- part 6, built-in types from test.support import run_with_locale -import collections +import collections.abc +import inspect import pickle import locale import sys @@ -1172,5 +1173,37 @@ class SimpleNamespaceTests(unittest.TestCase): self.assertEqual(ns, ns_roundtrip, pname) +class CoroutineTests(unittest.TestCase): + def test_wrong_args(self): + class Foo: + def __call__(self): + pass + def bar(): pass + + samples = [Foo, Foo(), bar, None, int, 1] + for sample in samples: + with self.assertRaisesRegex(TypeError, 'expects a generator'): + types.coroutine(sample) + + def test_genfunc(self): + def gen(): + yield + + self.assertFalse(isinstance(gen(), collections.abc.Coroutine)) + self.assertFalse(isinstance(gen(), collections.abc.Awaitable)) + + self.assertIs(types.coroutine(gen), gen) + + self.assertTrue(gen.__code__.co_flags & inspect.CO_ITERABLE_COROUTINE) + self.assertFalse(gen.__code__.co_flags & inspect.CO_COROUTINE) + + g = gen() + self.assertTrue(g.gi_code.co_flags & inspect.CO_ITERABLE_COROUTINE) + self.assertFalse(g.gi_code.co_flags & inspect.CO_COROUTINE) + self.assertTrue(isinstance(g, collections.abc.Coroutine)) + self.assertTrue(isinstance(g, collections.abc.Awaitable)) + g.close() # silence warning + + if __name__ == '__main__': unittest.main() |