[Python-ideas] Allow async defs for __await__
Thomas Gläßle
t_glaessle at gmx.de
Thu Feb 28 15:23:13 EST 2019
Hi,
I suggest to allow defining awaitable classes in terms of `async def`-s:
class FeynmanAlgorithm:
async def __await__(self):
await self.write_down_the_problem()
await self.think_real_hard()
await self.write_down_the_solution()
IMO, this would be the "one obvious way" for anyone who's using it for
anything other than defining low-level primitives. It is also easier to
teach and and more consistent with how you can define iterables - where
`__iter__` is explicitly allowed to be defined in terms of generator
function (which I would consider the equivalent to the async defs of
awaitables).
The implementation could be as easy as calling `__await__` as long as
the returned object is not an iterator instead of going straight to
TypeError.
Currently `__await__` must return an iterator, and one has to define a
separate function or method (or use a decorator) to do the same, e.g.:
class FeynmanAlgorithm:
def __await__(self):
return self.perform().__await__()
async def perform(self):
[...]
IMO, this is far from obvious and has been subject to a few subtle
differences between python versions. For example:
import asyncio
class Waiter:
def __await__(self):
return asyncio.sleep(1).__await__()
asyncio.get_event_loop().run_until_complete(
asyncio.ensure_future(Waiter()))
works fine on python 3.7 but fails on python 3.5 with "AttributeError:
'generator' object has no attribute '__await__'" because some awaitables
are implemented using generator type coroutines.
Adding to potential confusion, `async def __await__` seems to be work in
certain situations:
import asyncio
class Waiter:
async def __await__(self):
await asyncio.sleep(1)
# this works on py3.7:
asyncio.get_event_loop().run_until_complete(
asyncio.ensure_future(Waiter()))
but, awaiting them in a coroutine context causes an error:
async def foo():
await Waiter()
# "TypeError: __await__() returned a coroutine":
asyncio.get_event_loop().run_until_complete(
asyncio.ensure_future(foo()))
In earlier python versions (at least up to 3.5) the situation is
somewhat reversed. The `yield from` analogue of the above used to work
in coroutine context:
import asyncio
class Waiter:
def __iter__(self):
yield from asyncio.sleep(1)
@asyncio.coroutine
def foo():
yield from Waiter()
# works on py3.5:
asyncio.get_event_loop().run_until_complete(
asyncio.ensure_future(foo()))
While the following doesn't:
# "TypeError: An asyncio.Future, a coroutine or an awaitable is
required":
asyncio.get_event_loop().run_until_complete(
asyncio.ensure_future(Waiter()))
What do you think?
Kind regards,
Thomas
See also:
https://stackoverflow.com/questions/33409888/how-can-i-await-inside-future-like-objects-await
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 963 bytes
Desc: OpenPGP digital signature
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20190228/08bf56d4/attachment.sig>
More information about the Python-ideas
mailing list