> 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).
> 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.
> 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().
> 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.