async def inner() -> None:
print("inner")
async def main() -> None:
print("main start")
await inner()
print("main end")
if __name__ == "__main__":
import asyncio
asyncio.run(main())
and I added some print() in asyncio's event loop
def run_forever(self):
"""Run until stop() is called."""
try:
self._thread_id = threading.get_ident()
sys.set_asyncgen_hooks(firstiter=self._asyncgen_firstiter_hook,
finalizer=self._asyncgen_finalizer_hook)
events._set_running_loop(self)
print("start loop in run_forever"). # here!
while True:
self._run_once()
if self._stopping:
break
def _run_once(self):
"""Run one full iteration of the event loop.
This calls all currently ready callbacks, polls for I/O,
schedules the resulting callbacks, and finally schedules
'call_later' callbacks.
"""
print("_run_once called!") # here!
sched_count = len(self._scheduled)
if (sched_count > _MIN_SCHEDULED_TIMER_HANDLES and
...
_run_once()
start loop in run_forever
_run_once called!
main start
inner
main end
_run_once called!
start loop in run_forever
_run_once called!
_run_once called!
start loop in run_forever
_run_once called!
_run_once called!
Why there are three "start loop in run_forever" messages?
The loop is started 3 times in asyncio.run
(see the source code in asyncio/runners.py
):
main
Why is the whole program executed in one loop iteration? (Hoping I got the question right)
await another_coro()
does not interact directly with the event loop. It is very similar to yield from
, i.e. it builds a bi-directional connection with the another_coro()
and the program continues. If that coroutine awaits yet another coroutine etc., a pipeline of all these yield from
-s is formed. Only await future_not_done_yet
interacts with the loop, because it sends that future through that pipeline to the event loop (very similar to yield
) and the loop reacts: it marks the current task as waiting for the just received future and schedules (with call_soon
) another task from the set of runnable tasks to be run during the next loop iteration.