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