[Python-ideas] Add hooks to asyncio lifecycle
desmoulinmichel at gmail.com
Tue Jun 12 05:33:56 EDT 2018
> I still don't get it... If you have a framework you presumably have an
> entry point. Why can't you set up your policy in that entrypoint? Why
> would a user attempt to change the policy at runtime (you haven't
> listed examples of libraries that do this)?
Not at run time. But several libraries have several entry points, that
you must use in proper order, and setup correctly. Locking helps with
finding when you do an improper setup without reading all documentation
for all libs completely to make sure you didn't miss a corner case.
It will also help project owners because people will come to the bug
tracker saying "I have LockedPolicyError" when I do that, which is a lot
easier to solve than "I have an AttributeError on a None object".
I see a lot of "I want to
> protect users from ..." arguments but I haven't yet seen "this and
> that happened in production and we haven't been able to debug what
> happened for a while".
It's like with asyncio.get_event_loop. We haven't seen the problem for a
a long time. Then eventually, people starting to use several loops in
several threads, and they said that it caused a problem.
It's the same here. I'm advising that we don't wait for people to have
the problem to solve it.
> Do you handle cases when people install a blocking logging handler in
> their async application?
We can't prevent that without a great cost. It's terribly complicated
problem, especially since logging is blocking and thread safe by design.
There is not even good documentation on the best practice on using
logging with asyncio. Most people have no idea how to do it (although in
my experience, most dev use the logging module incorrectly outside of
asyncio too). Twisted rewrote an entire logging system to solve that
Locking the policy is not a great cost. And it's about creating a user
Do you handle cases when a malfunctioning
> sys.excepthook is installed?
When I override sys.excepthook, I take great care of backuping the old
hook, and calling it in my hook. And I really wish the stdlib had
something built in to do that cleanly, because I'm pretty sure most lib
overriding sys.excepthook all do that in a different way, if they do it
at all. Every time I use something to help with stack trace (coloring,
auto logging, etc), I have to read the source code to check they are
compatible. I should not have to do that.
What about cases when users accidentally
> import gevent somewhere in their asyncio application and it
> monkeypatches the 'socket' module (this is a real horror story, by the
Monkey patching is not officially supported by anything, anywhere, so
this argument is moot.
But it opens currently another door for my argument: currently the only
way to make sure nobody erase our policy is to monkey patch the function
to set the policy. Do we really want monkey patch to be the only
solution to this problem ?
My point is that there are so many things that users can do
> that will break any framework, be it asyncio or django or trio.
Yes, eventually the question is how easily and cleanly can we provide a
solution to avoid the problem or help to debug it.
Django is a good example. I run sanity checks on common errors on your
model on startup to ease your life as a dev, and the life on the
maintainers and their bug tracker.
> This sounds like "if something can happen it will happen" kind of
The reason I bring get_event_loop on the table is that I knew years ago
when I read that source code that it would come back to bite us. And I
said nothing because I assume the core devs would have though of that
and that I was mistaken.
But even if I have spoken up then, my experience with Python-idea is
that most of the time, people would have told me "no". They would have
told me that "nobody is going to do that".
I'm getting used to it. It took me a lot of time of "path should inherit
from strings" to finally have enough people involved that we get a
solution to the problem (which ended up being __fspath__). At the
beginning, the answer was "no, there is no problem".
So basically, now I speak up, knowing that people will say "no". But at
least I said it. Maybe in 2 years we will go back to that an implement
it, or another solution to this problem.
but I haven't yet seen good examples of real code that suffers
> from non-locked policies. Using the nurseries example doesn't count,
> as this is something that we want to have as a builtin functionality
> in 3.8.
So in we will be able to use that in 2 years.
> Locking policies can lead to more predictable user experience; OTOH
> what happens if, say, aiohttp decides to lock its policy to use uvloop
> and thus make it impossible for its users to use tokio or some other
> loop implementation?
That would be very, very good.
It would make explicit that aiohttp is:
- using a custom policy
- assuming it's the only one
- not allowing another lib to use one
- rely on it to work
- has no mechanism to allow to cohabit
This would mean user would learn very quickly and easily about those
issues, report it to the bug tracker if they matter and allow a debate
and a solution.
It's not the case for aiohttp though.
Another benefit is that this is a universal process. Once we get locking
in place, all libs will quickly realize if they do something wrong.
While right now, we just can't know.
Technically, Go and JS already lock those... by not providing access to
any of the machinery. So they don't have the problem. I wish the event
loop never had a Python API in the first place, so that the only way to
extend it would be in C. But this ship has sailed.
Trio nursery is a good example of defensive programming too : can you
prove that a lib is calling ensure_future() without proper managing it's
lifecycle ? Yes but it requires a lot of work. Can people use
ensure_future correctly ? Yes but we know some will mess up. I certainly
So it's easier to provide a proper way to do things.
Locking the policy is providing a clean and easy way to do things.
More information about the Python-ideas