Author scoder
Recipients asvetlov, gvanrossum, ncoghlan, python-dev, scoder, vstinner, yselivanov
Date 2015-05-28.03:30:02
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1432783802.3.0.484424800159.issue24017@psf.upfronthosting.co.za>
In-reply-to
Content
I added a couple of review comments to patch 6, but since no-one has responded so far, I guess they simply haven't been noticed. So I'll just repeat them here.

1)
getawaitablefunc / aiternextfunc / getaiterfunc

Is there a reason why these need to have their specific C type name instead of just reusing unaryfunc, or at least the existing iternextfunc / getiterfunc? They are unprefixed global names in the C namespace and I think we should be careful when adding more of those.

2)
Awaitable.register(Coroutine)

I think this is incorrect. A Coroutine is not Awaitable unless it also implements "__await__". How else should it be awaited?

3)
I propose to use this wrapping code as a fallback for types.coroutine() in the case that a Generator (ABC) is passed instead of a generator (yield):

  class types_coroutine(object):
    def __init__(self, gen):
        self._gen = gen

    class as_coroutine(object):
        def __init__(self, gen):
            self._gen = gen
            self.send = gen.send
            self.throw = gen.throw
            self.close = gen.close

        def __await__(self):
            return self._gen

    def __call__(self, *args, **kwargs):
        return self.as_coroutine(self._gen(*args, **kwargs))

Note that the resulting Awaitable Coroutine type is not an Iterable. This differs from a (yield) coroutine, but it matches the Coroutine and Awaitable protocols, and the intention to separate both in order to avoid mistakes on user side.


Additionally, regarding the tests:
4)

    def test_func_2(self):
        async def foo():
            raise StopIteration

        with self.assertRaisesRegex(
                RuntimeError, "generator raised StopIteration"):

            run_async(foo())

Why is this actually necessary? I'm aware that it's also mentioned in the PEP, but is there an actual reason why a coroutine should behave the same as a generator here? Is it just an implementation detail for legacy reasons because generators and coroutines happen to share the same type implementation? (I don't think they need to, BTW.)

5)
    def test_func_8(self):
        @types.coroutine
        def bar():
            return (yield from foo())

        async def foo():
            return 'spam'

        self.assertEqual(run_async(bar()), ([], 'spam') )

I find it surprising that this works at all. Yield-from iterates, and a coroutine is not supposed to be iterable, only awaitable (at least, that's what all error messages tell me when I try it). So why should "yield from" work on them? What if foo() was not an Iterable but a Coroutine? Should "yield from" then call "__await__" on it internally? I would find that *really* surprising, but given the above, I think it would be necessary to achieve consistency.
History
Date User Action Args
2015-05-28 03:30:02scodersetrecipients: + scoder, gvanrossum, ncoghlan, vstinner, asvetlov, python-dev, yselivanov
2015-05-28 03:30:02scodersetmessageid: <1432783802.3.0.484424800159.issue24017@psf.upfronthosting.co.za>
2015-05-28 03:30:02scoderlinkissue24017 messages
2015-05-28 03:30:02scodercreate