I'd like to add a new functionality to an existing awaitable class by subclassing it.
Let's start with a very simple base class creating objects which asynchronously return 99 after a short sleep. The subclass should just add +1 to the result.
I can't find the proper way to use super() to refer to the base class.
import asyncio
class R99:
def __await__(self):
loop = asyncio.get_event_loop()
fut = loop.create_future()
loop.call_later(0.5, fut.set_result, 99)
return fut.__await__()
class R100(R99):
async def add1(self):
v = await R99()
#v = await super().__await__() # <== error
return v + 1
def __await__(self):
return self.add1().__await__()
async def test():
print(await R99())
print(await R100())
asyncio.get_event_loop().run_until_complete(test())
The await method must return an iterator, so you can make it a generator and use the yield from syntax:
class R100(R99):
def __await__(self):
v = yield from super().__await__()
return v + 1
If you are allowed to modify R99, you can make __await__() invoke an actual coroutine, which can chain to super() in the regular way:
import asyncio
class R99:
async def await_coro(self):
loop = asyncio.get_event_loop()
fut = loop.create_future()
loop.call_later(0.5, fut.set_result, 99)
return await fut
def __await__(self):
return self.await_coro().__await__()
class R100(R99):
async def await_coro(self):
v = await super().await_coro()
return v + 1
If that is not an option, @Vincent's answer precisely explains how to chain from one __await__ to another. Note that you were quite correct in thinking that await is the new yield from - it is, and there is normally no reason to use yield from in newly written asyncio code. (This of course doesn't apply to non-asyncio-related generators that delegate to sub-generators; those are welcome to keep using yield from.)
However, by implementing __await__() you are dropping into the lower-level API which uses generators to implement coroutines. At this level yield suspends the coroutine, returning control to the event loop, and yield from delegates to another generator that implements a coroutine. In new code the only valid purpose for this layer is to implement an awaitable object without using the interpreter, e.g. in Python/C or Cython. This is done by providing an __await__ that returns an iterator, as shown here. The resulting object is equivalent to an async def.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With