python-interface icon indicating copy to clipboard operation
python-interface copied to clipboard

Check async function ?

Open touilleMan opened this issue 6 years ago • 3 comments

It seems the module currently don't make difference between sync and async functions:

>>> import interface
>>> class IFoo(interface.Interface):
...   async def do_async(self):
...     pass
... 
>>> class BadFoo(interface.implements(IFoo)):
...   def do_async(self):
...     pass
... 
>>> # No error has been raised !

touilleMan avatar Apr 19 '18 09:04 touilleMan

Hmm. I think it's an interesting question whether interface should reject BadFoo in your example.

The contract that you probably care about as a consumer of IFoo is that do_async returns an awaitable, but it's perfectly possible to write a non-async function that returns an awaitable.

For example, this would be probably be a valid implementation of IFoo that doesn't use an async def:

class MyAwaitable:

    def __init__(self, value):
        self.value = value

    def __await__(self):
        return self.value
        yield


class ValidFoo(interface.implements(IFoo)):

    def do_async(self):
        return MyAwaitable(5)

ssanderson avatar Apr 19 '18 14:04 ssanderson

The contract that you probably care about as a consumer of IFoo is that do_async returns an awaitable, but it's perfectly possible to write a non-async function that returns an awaitable.

I agree, however this is a pretty unorthodox way of writting async function... well orthodoxy about async function have changed a lot since Python 3.4, but now async/await have landed and are supported by all major frameworks (asyncio, twisted, tornado, curio, trio etc.)

So considering your example, I would expect it to be implemented like:

class ValidFoo(interface.implements(IFoo)):

    async def do_async(self):
        return await MyAwaitable(5)

This way we respect the async interface requirement, even if we use a custom awaitable.

On a more theoretical point of view, async/await has been designed to easily recognize asynchronous blocks and synchronization points (otherwise generator with yield would have done the job fine ^^). I would say defining interface share a similar goal as another way of making Python code more explicit (as opposed to just have something that runs).

touilleMan avatar Apr 21 '18 08:04 touilleMan

On a more theoretical point of view, async/await has been designed to easily recognize asynchronous blocks and synchronization points (otherwise generator with yield would have done the job fine ^^). I would say defining interface share a similar goal as another way of making Python code more explicit (as opposed to just have something that runs).

@touilleMan I buy that argument (sorry for the long delay on replying). I took a crack at implementing this in https://github.com/ssanderson/interface/pull/17/ if you want to take a look.

ssanderson avatar May 15 '18 20:05 ssanderson