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