Check async function ?
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 !
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)
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).
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.