Add support for top-level await

I want top-level await without any boilerplate code or setup.
Just write a “await” statement on line 1 without any indention.

#!/usr/bin/env python3
import asyncio
await asyncio.sleep(1)

I don’t want to have to call asyncio.run(main()), and I don’t want to have to put the await inside an async function.

How would Python know how to setup and call whichever event loop you’re wanting to use? asyncio isn’t the only library around, the async syntax was designed to be agnostic in that sense. If you’re importing a module I suppose you could make it so that’s awaitable, but how do you start this off?

Python is suppose to be a high-level language, where developer should focus on their domain, not about platform-specific, architecture-specific stuff or other details of underlying abstractions.

For example, when I open a TCP connection, I don’t care if the machine is x86 or ARM, or if its Window or Linux, or if the network is Ethernet or Wi-Fi, nor do I care if the async runtime is Foo or Bar.

Python could default to a async runtime.

But you do care whether it is a TCP, UDP, SSL, Telnet, HTTP, etc.

Which async runtime do you think Python should default to, and why?

I don’t know which async runtime Python should default to. I don’t think I care either. I didn’t know there existed many. Probably the one in the “asyncio” module, no idea why though. I’d be happy with either one, I don’t care.

1 Like

In addition to the problem with choosing an async runtime, the import statement isn’t async, so if you want to do import foo then foo.py can’t contain top-level awaits. Which means that this would be restricted to just the single top-level script at most.

I guess now that we have PyCF_ALLOW_TOP_LEVEL_AWAIT though you could implement a command-line tool that runs a python script with top-level await and an event loop, like asyncpython foo.py.

1 Like

We can extend the CLI of the asyncio module to run files:

python -m asyncio myscript.py arg1 arg2

Currently it works only as REPL.

4 Likes

Good idea, easy to implement.

I don’t want my imports to execute any code anyways, that is just bad behavior, code should be executed when I call the functions in the module, not when I import it.

So if I import a module, and it have a async def function then I guess I can still call it even if the import statement is not async.

Yes, we could extend the CLI in that way, and maybe some would like it, but personally I don’t want to tell users how to execute my script, I just write the code, I can’t tell users how they should execute my script.

They will execute it as:

$ chmod +x myscript.py
$ ./myscript.py

But you already do, using the shebang line. Change it to #!/usr/bin/env -S python3 -m asyncio.
It might not work on all systems, but that’s probably fixable in launchers.

1 Like

This is a good suggestion, then I can have that as part of my code, instead of giving users any explicit instructions. Downside is that it does not work on Windows though and it is some extra boilerplate code.

The py launcher for Windows does parse shebang lines, so this might actually work cross platform.

Just noting that as of this posting, this doesn’t work. Serhiy Storchaka suggested this might be a good extension of the asyncio module’s command-line interface, but it hasn’t been implemented yet. So using this as a shebang will just land you in the asyncio REPL.

1 Like