|
1 | 1 | import inspect
|
2 | 2 | import types
|
3 | 3 | import unittest
|
| 4 | +import contextlib |
4 | 5 |
|
5 | 6 | from test.support.import_helper import import_module
|
6 | 7 | from test.support import gc_collect
|
7 | 8 | asyncio = import_module("asyncio")
|
8 | 9 |
|
9 | 10 |
|
| 11 | +_no_default = object() |
| 12 | + |
| 13 | + |
10 | 14 | class AwaitException(Exception):
|
11 | 15 | pass
|
12 | 16 |
|
@@ -45,6 +49,37 @@ async def iterate():
|
45 | 49 | return run_until_complete(iterate())
|
46 | 50 |
|
47 | 51 |
|
| 52 | +def py_anext(iterator, default=_no_default): |
| 53 | + """Pure-Python implementation of anext() for testing purposes. |
| 54 | +
|
| 55 | + Closely matches the builtin anext() C implementation. |
| 56 | + Can be used to compare the built-in implementation of the inner |
| 57 | + coroutines machinery to C-implementation of __anext__() and send() |
| 58 | + or throw() on the returned generator. |
| 59 | + """ |
| 60 | + |
| 61 | + try: |
| 62 | + __anext__ = type(iterator).__anext__ |
| 63 | + except AttributeError: |
| 64 | + raise TypeError(f'{iterator!r} is not an async iterator') |
| 65 | + |
| 66 | + if default is _no_default: |
| 67 | + return __anext__(iterator) |
| 68 | + |
| 69 | + async def anext_impl(): |
| 70 | + try: |
| 71 | + # The C code is way more low-level than this, as it implements |
| 72 | + # all methods of the iterator protocol. In this implementation |
| 73 | + # we're relying on higher-level coroutine concepts, but that's |
| 74 | + # exactly what we want -- crosstest pure-Python high-level |
| 75 | + # implementation and low-level C anext() iterators. |
| 76 | + return await __anext__(iterator) |
| 77 | + except StopAsyncIteration: |
| 78 | + return default |
| 79 | + |
| 80 | + return anext_impl() |
| 81 | + |
| 82 | + |
48 | 83 | class AsyncGenSyntaxTest(unittest.TestCase):
|
49 | 84 |
|
50 | 85 | def test_async_gen_syntax_01(self):
|
@@ -374,6 +409,12 @@ def tearDown(self):
|
374 | 409 | asyncio.set_event_loop_policy(None)
|
375 | 410 |
|
376 | 411 | def check_async_iterator_anext(self, ait_class):
|
| 412 | + with self.subTest(anext="pure-Python"): |
| 413 | + self._check_async_iterator_anext(ait_class, py_anext) |
| 414 | + with self.subTest(anext="builtin"): |
| 415 | + self._check_async_iterator_anext(ait_class, anext) |
| 416 | + |
| 417 | + def _check_async_iterator_anext(self, ait_class, anext): |
377 | 418 | g = ait_class()
|
378 | 419 | async def consume():
|
379 | 420 | results = []
|
@@ -406,6 +447,24 @@ async def test_2():
|
406 | 447 | result = self.loop.run_until_complete(test_2())
|
407 | 448 | self.assertEqual(result, "completed")
|
408 | 449 |
|
| 450 | + def test_send(): |
| 451 | + p = ait_class() |
| 452 | + obj = anext(p, "completed") |
| 453 | + with self.assertRaises(StopIteration): |
| 454 | + with contextlib.closing(obj.__await__()) as g: |
| 455 | + g.send(None) |
| 456 | + |
| 457 | + test_send() |
| 458 | + |
| 459 | + async def test_throw(): |
| 460 | + p = ait_class() |
| 461 | + obj = anext(p, "completed") |
| 462 | + self.assertRaises(SyntaxError, obj.throw, SyntaxError) |
| 463 | + return "completed" |
| 464 | + |
| 465 | + result = self.loop.run_until_complete(test_throw()) |
| 466 | + self.assertEqual(result, "completed") |
| 467 | + |
409 | 468 | def test_async_generator_anext(self):
|
410 | 469 | async def agen():
|
411 | 470 | yield 1
|
@@ -569,6 +628,119 @@ async def do_test():
|
569 | 628 | result = self.loop.run_until_complete(do_test())
|
570 | 629 | self.assertEqual(result, "completed")
|
571 | 630 |
|
| 631 | + def test_anext_iter(self): |
| 632 | + @types.coroutine |
| 633 | + def _async_yield(v): |
| 634 | + return (yield v) |
| 635 | + |
| 636 | + class MyError(Exception): |
| 637 | + pass |
| 638 | + |
| 639 | + async def agenfn(): |
| 640 | + try: |
| 641 | + await _async_yield(1) |
| 642 | + except MyError: |
| 643 | + await _async_yield(2) |
| 644 | + return |
| 645 | + yield |
| 646 | + |
| 647 | + def test1(anext): |
| 648 | + agen = agenfn() |
| 649 | + with contextlib.closing(anext(agen, "default").__await__()) as g: |
| 650 | + self.assertEqual(g.send(None), 1) |
| 651 | + self.assertEqual(g.throw(MyError, MyError(), None), 2) |
| 652 | + try: |
| 653 | + g.send(None) |
| 654 | + except StopIteration as e: |
| 655 | + err = e |
| 656 | + else: |
| 657 | + self.fail('StopIteration was not raised') |
| 658 | + self.assertEqual(err.value, "default") |
| 659 | + |
| 660 | + def test2(anext): |
| 661 | + agen = agenfn() |
| 662 | + with contextlib.closing(anext(agen, "default").__await__()) as g: |
| 663 | + self.assertEqual(g.send(None), 1) |
| 664 | + self.assertEqual(g.throw(MyError, MyError(), None), 2) |
| 665 | + with self.assertRaises(MyError): |
| 666 | + g.throw(MyError, MyError(), None) |
| 667 | + |
| 668 | + def test3(anext): |
| 669 | + agen = agenfn() |
| 670 | + with contextlib.closing(anext(agen, "default").__await__()) as g: |
| 671 | + self.assertEqual(g.send(None), 1) |
| 672 | + g.close() |
| 673 | + with self.assertRaisesRegex(RuntimeError, 'cannot reuse'): |
| 674 | + self.assertEqual(g.send(None), 1) |
| 675 | + |
| 676 | + def test4(anext): |
| 677 | + @types.coroutine |
| 678 | + def _async_yield(v): |
| 679 | + yield v * 10 |
| 680 | + return (yield (v * 10 + 1)) |
| 681 | + |
| 682 | + async def agenfn(): |
| 683 | + try: |
| 684 | + await _async_yield(1) |
| 685 | + except MyError: |
| 686 | + await _async_yield(2) |
| 687 | + return |
| 688 | + yield |
| 689 | + |
| 690 | + agen = agenfn() |
| 691 | + with contextlib.closing(anext(agen, "default").__await__()) as g: |
| 692 | + self.assertEqual(g.send(None), 10) |
| 693 | + self.assertEqual(g.throw(MyError, MyError(), None), 20) |
| 694 | + with self.assertRaisesRegex(MyError, 'val'): |
| 695 | + g.throw(MyError, MyError('val'), None) |
| 696 | + |
| 697 | + def test5(anext): |
| 698 | + @types.coroutine |
| 699 | + def _async_yield(v): |
| 700 | + yield v * 10 |
| 701 | + return (yield (v * 10 + 1)) |
| 702 | + |
| 703 | + async def agenfn(): |
| 704 | + try: |
| 705 | + await _async_yield(1) |
| 706 | + except MyError: |
| 707 | + return |
| 708 | + yield 'aaa' |
| 709 | + |
| 710 | + agen = agenfn() |
| 711 | + with contextlib.closing(anext(agen, "default").__await__()) as g: |
| 712 | + self.assertEqual(g.send(None), 10) |
| 713 | + with self.assertRaisesRegex(StopIteration, 'default'): |
| 714 | + g.throw(MyError, MyError(), None) |
| 715 | + |
| 716 | + def test6(anext): |
| 717 | + @types.coroutine |
| 718 | + def _async_yield(v): |
| 719 | + yield v * 10 |
| 720 | + return (yield (v * 10 + 1)) |
| 721 | + |
| 722 | + async def agenfn(): |
| 723 | + await _async_yield(1) |
| 724 | + yield 'aaa' |
| 725 | + |
| 726 | + agen = agenfn() |
| 727 | + with contextlib.closing(anext(agen, "default").__await__()) as g: |
| 728 | + with self.assertRaises(MyError): |
| 729 | + g.throw(MyError, MyError(), None) |
| 730 | + |
| 731 | + def run_test(test): |
| 732 | + with self.subTest('pure-Python anext()'): |
| 733 | + test(py_anext) |
| 734 | + with self.subTest('builtin anext()'): |
| 735 | + test(anext) |
| 736 | + |
| 737 | + run_test(test1) |
| 738 | + run_test(test2) |
| 739 | + run_test(test3) |
| 740 | + run_test(test4) |
| 741 | + run_test(test5) |
| 742 | + run_test(test6) |
| 743 | + |
572 | 744 | def test_aiter_bad_args(self):
|
573 | 745 | async def gen():
|
574 | 746 | yield 1
|
|
0 commit comments