[Python-ideas] Add hooks to asyncio lifecycle

Michel Desmoulin desmoulinmichel at gmail.com
Tue Jun 5 08:47:44 EDT 2018

After years of playing with asyncio, I'm still having a harder time
using it than any other async architecture around. There are a lot of
different reasons for it, but this mail want to address one particular one:

The event loop and policy can be tweaked at any time, by anyone.

Now, it's hard enough to have to deal, manually, with a low-level event
loop. But having it exposed that much, and it being that flexible means
any code can just do whatever it wants with it, and make a mess.

Several things in particular, comes to mind:

- Changing the event loop policy
- Changing the event loop
- Spawning a new loop
- Starting the loop
- Stopping the loop
- Closing the loop

Now, if you want to make any serious project with it, you currently have
to guard against all of those, especially if you want to have proper
cleanup code, good error message and a decent debugging experience.

I tried to do it for one year, and currently, it's very hard. You have a
lot of checks to make, redundantly in a lot of places. Some things can
only be done by providing a custom event policy/loop yourself, and, of
course, expecting (aka documenting and praying) that it's used.

For a lot of things, when it breaks, the people that haven't read the
doc in depth will have a hard time to understand the problem after the fact.

Sometimes, it's just that your code use somebody else code that is not
here to read your doc anymore. Now you have to check their code to
understand what they are doing that breaks your expectations about the
loop / policy or workflow.

Barring the creating of an entire higher level framework that everybody
will agree on using and that makes messing up way harder, we can improve
this situation by adding hooks to those events.

I hence propose to add:

- asyncio.on_change_policy(cb:Callable[[EventLoopPolicy,
EventLoopPolicy], EventLoopPolicy])

- asyncio.on_set_event_loop(cb:Callable[[EventLoop, EventLoop], EventLoop])

- asyncio.on_create_event_loop(cb:Callable[[EventLoop], EventLoop])

- EventLoop.on_start(cb:Callable[EventLoop])

- EventLoop.on_stop(cb:Awaitable[EventLoop])

- EventLoop.on_close(cb:Callable[EventLoop])

- EventLoop.on_set_debug_mode(cb:Callable[[loop]])

This would allow to implement safer, more robust and easier to debug
code. E.G:

- you can raise a warning stating that if somebody changes the event
policy, it must inherit from your custom one or deal with disabled features

- you can raise an exception on loop swap and forbid it, saying that
your small script doesn't support it yet so that it's easy to understand
the limit of your code

- you can hook on the event loop life cycle to automatically get on
board, or run clean up code, starting logging, warn that you were
supposed to start the loop yourself, etc

More information about the Python-ideas mailing list