On 2020-06-13 2:32 a.m., Kyle Stanley wrote:
> An await outside async def, would map to awaitable.run().
> A call to baz() would thus create a coroutine foo(), and then run() it.
-1. The `await` expression already has a well-defined purpose of
suspending the coroutine it's used within until the awaitable is
completed. I think this change in behavior would be rather confusing,
and results in minimal (if any) practical benefit since you can
already accomplish the main intended behavior of effectively running
coroutines as blocking subroutines (see next section).
It'd also let you force blocking mode by calling .run() directly,
whereas "await" would let the context make the decision for you.
> In particular, this could allow one to turn any arbitrary async
coroutine into a blocking subroutine, as long as it supports this
run() protocol. This would be great for refactoring existing blocking
code into async - just use stuff as if it's sync, then when you're
done, switch up your function with async def, and change relevant
calls into it into "await" calls. Rinse and repeat until you're ready
to add a top-level event loop.
This can already be effectively accomplished by gradually converting
the synchronous code into coroutines and running them individually
with asyncio's loop.run_until_complete(coro()) (which blocks until
finished) from a subroutine while the other parts of the code are
still transitioning to async. However, the bulk of the effort is
considering the architecture of the program, specifically analyzing
the parts that need to be completed sequentially vs those that can be
completed concurrently, and then figuring out exactly when/where the
results are needed. So, I don't think that making an await expression
outside of a coroutine function an alias to an awaitable.run() and
effectively doing the same thing as loop.run_until_complete() would
save substantial development time, and it adds additional unneeded
complexity to the await expression for little to no practical benefit.
Also, I very much appreciate knowing that when I see "await" used, I'm
certain it's being called within a coroutine and more importantly, the
expected behavior. Having to check whether the function is awaited in
is a coroutine or a subroutine to determine the behavior could prove
to be quite cumbersome in non-trivial programs. A significant part of
why await was added in the first place was to avoid the ambiguity
present with "yield from". In general, I think there's a significant
advantage in having a singular purpose for a keyword, rather than
changing based on where it's used.
There's a huge difference between "yield from" and "await".
Additionally, what if I'm not using asyncio?
> Optionally, this would also allow us to deprecate blocking APIs
(like open(), etc
Huge -1 for deprecating the existing blocking APIs. Although
converting some IO-bound functions to asynchronous equivalents can
yield significant performance benefits (when structured optimally),
it's not always worthwhile to do so. Particularly in situations where
the underlying OS call doesn't have asynchronous support (frequently
the case for async file system operations), or when the intended use
case for the program wouldn't benefit from the performance
improvements enough to justify the added complexity and development
time. So blocking IO-bound functions still have a significant purpose,
and should definitely __not__ be deprecated. Even if there was a
working automatic conversion process to place an "await" before each
of the calls, it would still cause a lot of breakage and add
additional noise to the code that wouldn't provide additional useful
context.
Also, consider the amount of times that various Python tutorials and
textbooks use open() that would all have to be rewritten because it no
longer works without "await" before it. Realistically speaking, I
don't think there's any chance of something as commonly used as open()
being deprecated, regardless how far out the removal would be. Even
if/when async file system support at the OS level becomes the
mainstream (which it currently isn't), I still don't think it would be
worth breaking open().
As I said previously, you can still guarantee blocking behaviour by
explicitly calling .run(), instead of using await. There is a lot of
friction in refactoring to async/await, including asyncio being
suboptimal and the fact that you can't really call async functions (but
in blocking mode) from non-async code.
> and asyncio, and merge them into one TBD API. Perhaps as part of
Python 4.
Even if I agreed with the main proposal, I would still be against this
part. Stdlib module deprecation is a very long process that has to be
strongly justified, considering the amount of breakage that would
occur from its eventual removal. So even with the assumption that the
proposal would provide some real benefit in the transition from sync
to async (which I disagree with), I don't think this would come even
close to justifying a full deprecation of asyncio into a separate "TBD
API".
Thanks for sharing the idea Soni, but I would personally be opposed to
every component of the proposal.
What's the point of having async, if it has to be such a massive pain to
refactor my existing code into? Some of the issues caused by only
partially switching to async, namely that you have to go out of your way
to call an async function from non-async code, feel to me like
unnecessary friction. I understand and love having 2 calling
conventions, but, heck even having coroutines carry a run() method would
be better than what we have today.
And then there's the issue of nested event loops. It just makes things
even more problematic in hybrid codebases. :/
Consider my proposal the best of both worlds: you get all the benefits
of async/await, and all the benefits of blocking APIs, and you get to
choose whether to use autodetection or explicitly force the latter. (you
could also force the existence of a nested event loop, but I don't think
that'd be a good idea.)
On Fri, Jun 12, 2020 at 7:20 PM Soni L. <fakedme...@gmail.com
<mailto:fakedme%2...@gmail.com>> wrote:
On 2020-06-12 5:47 p.m., J. Pic wrote:
Hi all,
Currently, you can not use await outside an async function, the
following code:
async def lol():
return 'bar'
def test():
return await lol()
print(test())
Will fail with: SyntaxError: 'await' outside async function
Of course, you can use asyncio.run and then it works fine:
import asyncio
async def lol():
return 'bar'
def test():
return asyncio.run(lol())
print(test())
Why not make using await do asyncio.run "behind the scenes" when
called outside async function ?
Thank you in advance for your replies
What if we extend awaitables to be optionally "runnable"?
An await outside async def, would map to awaitable.run(). In the
specific case of coroutines, this would also automatically
propagate down. E.g.
async def foo():
await bar()
def baz()
await foo()
A call to baz() would thus create a coroutine foo(), and then
run() it. By my proposed semantics, the await bar() would also
call run() on the result of bar()!
In particular, this could allow one to turn any arbitrary async
coroutine into a blocking subroutine, as long as it supports this
run() protocol. This would be great for refactoring existing
blocking code into async - just use stuff as if it's sync, then
when you're done, switch up your function with async def, and
change relevant calls into it into "await" calls. Rinse and repeat
until you're ready to add a top-level event loop.
Optionally, this would also allow us to deprecate blocking APIs
(like open(), etc) and asyncio, and merge them into one TBD API.
Perhaps as part of Python 4. (e.g. turn existing open() into await
open(), particularly in non-async-def. I *think* this could be
done automatically as part of some sort of "3to4", even.)
--
∞
_______________________________________________
Python-ideas mailing list --python-ideas@python.org
<mailto:python-ideas@python.org>
To unsubscribe send an email topython-ideas-le...@python.org
<mailto:python-ideas-le...@python.org>
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived
athttps://mail.python.org/archives/list/python-ideas@python.org/message/LOCYSYVRKXI45QQJOLYGZV6H2CBYTB7F/
Code of Conduct:http://python.org/psf/codeofconduct/
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
<mailto:python-ideas@python.org>
To unsubscribe send an email to python-ideas-le...@python.org
<mailto:python-ideas-le...@python.org>
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at
https://mail.python.org/archives/list/python-ideas@python.org/message/Y73VH53OXZZ2GDWRVGRX2B76U3TSHYZF/
Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at
https://mail.python.org/archives/list/python-ideas@python.org/message/NI6BP5Z5RTB7OOOJCFMGM2ZBYW2EXVXM/
Code of Conduct: http://python.org/psf/codeofconduct/