Part of why Python 3.5's
async have some odd usage restrictions
Python 3.5 added a new system for coroutines and asynchronous
programming, based around new
await keywords (which
have the technical details written up at length in PEP 492). Roughly speaking, in
terms of coroutines implemented with
await replaces '
yield from' (and is
more powerful). So what's
async for? Well, it marks a function
that can use
await. If you use
await outside an
you'll get a syntax error. Functions marked
async have some odd
restrictions, too, such as that you can't use
from in them.
When I described doing coroutines with
yield from here, I noted that it was potentially error
prone because in order to make everything work you had to have an
unbroken chain of
yield from from top to bottom. Break the chain
yield instead of
yield from, and things wouldn't work.
And because both
yield from and
yield are used for regular
generators as well as coroutines, it's possible to slip up in
various ways. Well, when you introduce new syntax you can fix
issues like that, and that's part of why
have their odd rules.
A function marked
async is a (native) coroutine.
await can only
be applied to coroutines, which means that you can't accidentally
treat a generator like a coroutine the way you can with
from. Simplifying slightly, coroutines can only be invoked through
await; you can't call one or use them as a generator, for example
for something in coroutine(...):'. As part of not being
generators, coroutines can't use '
yield' or '
(And there's only
await, so you avoid the whole '
yield from' confusion.)
In other words, coroutines can only be invoked from coroutines and
they must be invoked using the exact mechanism that makes coroutines
work (and that mechanism isn't and can't be used for or by anything
else). The entire system is designed so that you're more or less
forced to create that unbroken chain of
awaits that makes it all
go. Although Python itself won't error out on
import time if you
try to call a
async function without
await (it just won't work
at runtime), there's probably Python static checkers that look for
this. And in general it's an easy rule to keep track of; if it's
async, you have to
await it, and this status is marked right
there in the function definition.
(Unfortunately it's not in the type of the function, which means
that you can't tell by just importing the module interactively and
then doing '
Sidebar: The other reason you can only use
Before Python 3.5, the following was completely valid code:
def somefunc(a1, b2): ... await = interval(a1, 10) otherfunc(b2, await) ...
In other words,
await was not a reserved keyword and so could be
legally used as the name of a local variable, or for that matter a
function argument or a global.
Had Python 3.5 made
await a keyword in all contexts, all such
code would immediately have broken. That's not acceptable for a
minor release, so Python needed some sort of workaround. So it's
not that you can't use
await outside of functions marked
it's that it's not a keyword outside of async functions. Since
it's not a keyword, writing something like '
await func(arg)' is
a syntax error, just as '
abcdef func(arg)' would be.
The same is true of
async, by the way:
def somefunc(a, b, async = False): if b == 10: async = True ....
Thus why it's a syntax error to use '
async for' or '
outside of an
async function; outside of such functions
isn't even a keyword so '
async for' is treated the same as '
(I'm sure this makes Python's parser that much more fun.)