[Python-Dev] PEP 492 vs. PEP 3152, new round (original) (raw)
Łukasz Langa lukasz at langa.pl
Fri Apr 24 23:37:04 CEST 2015
- Previous message (by thread): [Python-Dev] PEP 492 vs. PEP 3152, new round
- Next message (by thread): [Python-Dev] PEP 492 vs. PEP 3152, new round
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
On Apr 24, 2015, at 10:03 AM, Guido van Rossum <guido at python.org> wrote:
1. precise syntax of
async def
So I still preferasync def
.
Me too. Also, we would use a similar vocabulary to existing users of the feature. This is exactly how Hack does it: http://docs.hhvm.com/manual/en/hack.async.php <http://docs.hhvm.com/manual/en/hack.async.php>, how ECMAScript 7 proposes it: http://wiki.ecmascript.org/doku.php?id=strawman:async_functions <http://wiki.ecmascript.org/doku.php?id=strawman:async_functions> and similarly to how C# does it (async comes after the public/private modifier but before the return type): https://msdn.microsoft.com/en-us/library/hh156513.aspx <https://msdn.microsoft.com/en-us/library/hh156513.aspx>
2. do we need
async for
andasync with
Yes we do.
+1
(Though maybe we should consider
await for
andawait with
? That would have the advantage of making it easy to scan for all suspension points by searching for /await/. But being a verb it doesn't read very well.)
I’m on the fence here.
OT1H, I think “await for something in a_container” and “await with a_context_manager():” also read pretty well. It’s also more consistent with being the one way of finding “yield points”.
OTOH, “await with a_context_manager():” suggests the entire statement is awaited on, which is not true. “async with” is more opaque in this way, it simply states “there are going to be implementation-specific awaits inside”. So it’s more consistent with “async def foo()”.
All in all I think I’m leaning towards “async for” and “async with”. More importantly though, I’m wondering how obvious will the failure mode be when somebody uses a bare “for” instead of an “async for”. Ditto for “with” vs. “async with”. How much debugging will be necessary to find that it’s only a missing “async” before the loop?
Side note: to add to the confusion about syntax, Hack’s equivalent for “async for” - which doesn’t really translate well to Python - uses “await” and ties it to the iterable:
foreach ($list await as $input) { … }
The equivalent in Python would be:
for input in list await:
Notably, this is ugly and would be confused with for input in await list:
which means something different.
Also, this particular construct represents less than 0.01% of all “await” occurences in Facebook code, suggesting it’s not performance critical.
3. syntactic priority of
await
Yury, could you tweak the syntax for
await
so that we can write the most common usages without parentheses?
+1
Yury points out there was likely a reason this wasn’t the case for yield
in the first place. It would be good to revisit that. Maybe for yield itself, too?
4.
cocall
vs.await
I just can't get used to this aspect of PEP 3152, so I'm rejecting it.
+1
(Yury: PEP 492 is not accepted yet, but you're getting closer.)
May I suggest using the bat-signal to summon Glyph to confirm this is going to be helpful/usable with Twisted as well?
5. do we really need
_aiter_
and friendsThere's a lot of added complexity, but I think it's worth it. I don't think we need to make the names longer, the 'a' prefix is fine for these methods.
+1
6. StopAsyncException
I'm not sure about this. The motivation given in the PEP seems to focus on the need for
_anext_
to be async. But is this really the right pattern? What if we requiredait._anext_()
to return a future, which can either raise good oldStopIteration
or return the next value from the iteration when awaited? I'm wondering if there are a few alternatives to be explored around the async iterator protocol still.
So are you suggesting to pass the returned value in a future? In this case the future would need to be passed to anext, so the Cursor example from the PEP would look like this:
class Cursor: def init(self): self.buffer = collections.deque()
def _prefetch(self):
...
async def __aiter__(self):
return self
async def __anext__(self, fut):
if not self.buffer:
self.buffer = await self._prefetch()
if self.buffer:
fut.set_result(self.buffer.popleft())
else:
fut.set_exception(StopIteration)
While this is elegant, my concern is that one-future-per-iteration-step might be bad for performance.
Maybe consider the following. The async def
syntax decouples the concept of a coroutine from the implementation. While it’s still based on generators under the hood, the user no longer considers his “async function” to be a generator or conforming to the generator protocol. From the user’s perpective, it’s obvious that the return below means something different than the exception:
async def __anext__(self):
if not self.buffer:
self.buffer = await self._prefetch()
if not self.buffer:
raise StopIteration
return self.buffer.popleft()
So, the same way we added wrapping in RuntimeErrors for generators in PEP 479, we might add transparent wrapping in a _StopAsyncIteration for CO_COROUTINE.
7. compatibility with asyncio and existing users of it
+1, this is really important.
-- Best regards, Łukasz Langa
WWW: http://lukasz.langa.pl/ Twitter: @llanga IRC: ambv on #python-dev
-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://mail.python.org/pipermail/python-dev/attachments/20150424/00c46055/attachment.html>
- Previous message (by thread): [Python-Dev] PEP 492 vs. PEP 3152, new round
- Next message (by thread): [Python-Dev] PEP 492 vs. PEP 3152, new round
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]