On Mon, Oct 31, 2016 at 1:30 PM, Yury Selivanov <[email protected]> wrote:
> Guido, > > I’m with Andrew on this one. > And the reason is the misbehavior of get_event_loop(), right? (See below.) > > On Oct 31, 2016, at 10:27 AM, Guido van Rossum <[email protected]> wrote: > > > > I would suggest different guidelines for libraries than for > > applications: Libraries should be robust and always store their own > > loop. This is how asyncio itself works and how aiohttp (and other > > libraries you name) work. Their test suite (like asyncio's) should > > enforce this. > > > But applications (and fledgling libraries may do this too) should be > > allowed to assume a simpler model of the world: use coroutines (async > > def, await) for everything, never store the loop, happily use > > get_event_loop() when you really need it. And really, you should only > > need it for run_in_executor(). If you find yourself using call_later() > > you probably haven't quite figured out how to use coroutines properly. > > I don’t think people will follow this advice, because any application code > can one day be refactored to become reusable (a library). That’s why I > write my asyncio code defensively, passing the loop explicitly. This is > probably the only aspect about asyncio I don’t like. This is the main > complaint about asyncio that we hear *repeatedly* from all kinds of users. > > The core reason causing all these problems is that get_event_loop is > weakly defined. It doesn’t always return the current loop when called from > a coroutine. It might return the wrong one, that just happens to be the > “default”. > OK, I think I am seeing the problem now. > I know that you defend the current behaviour by saying that people should > only have one loop per process. But some people have many in one process. > That’s why we design libraries to handle the loop explicitly — that ensures > that the library will work regardless of what get_event_loop returns. > > The result is that all asyncio libraries accept the “loop” parameter in > their public APIs. All of them are architected to pass the loop explicitly > internally. And then, the asyncio end users aren’t sure how to use > asyncio. Python docs recommend (sometimes indirectly) to pass the loop > explicitly. Libraries and frameworks recommend that as well. > That's still a docs problem (many stdlib functions/methods have optional parameters that are almost always left None). > The end result of this is that asyncio programs always care “too much” > about the loop: mange it, store references to it, explicitly pass it to > coroutines. This makes asyncio code harder to understand to both advanced > and beginner users. > Right. > My opinion on this: > > 1. We need to fix “get_event_loop” to return the *current* event loop when > called from within a coroutine: > > async def coro(): > loop = asyncio.get_event_loop() > > This is something we can easily implement, because coroutines are *always* > running under some event loop. > I now agree. > 2. We should explain asyncio library authors that it is better to “hide” > the loop from the high-level API. The high level API should only consist > of coroutines, that don’t have a “loop” parameter at all. Because > get_event_loop is guaranteed to always return the correct loop, it will be > safe to use it. > > So instead of: > > async def main(loop): > db = asyncpg.connect(…, loop=loop) > loop = asyncio.get_event_loop() > loop.run_until_complete(main(loop)) > > people will always write this: > > async def main(): > db = asyncpg.connect(…) > loop = asyncio.get_event_loop() > loop.run_until_complete(main()) > I think my (feeble) point is that we could recommend this even without changing get_event_loop(), because the situation where get_event_loop() returns the wrong loop is perverse. Unfortunately we've gone down this path too long, so e.g. testing habits have developed that require explicitly passing the loop. :-( So I agree we should fix #1. > 3. Docs should say that the event loop is a low-level API. As you said, > if you call “loop.call_soon” from your application code, then something is > wrong. > > If we can shift asyncio libraries to be designed around coroutine-first > API, we can safely start caring much less about the loop. We will only use > it to run the main() coroutine of the program. > > This is something that curio does right — the event loop is what runs the > program, but the end user knows pretty much nothing about it. Coroutines > just work, because curio provides a *reliable* way for getting the > *current* loop from within a running coroutine context. > > This is a fully backwards compatible change. We even have a PR to do > this: https://github.com/python/asyncio/pull/355. That PR might need > another review pass, but the idea is there. > > Thank you, > Yury > > Thanks! We can fix this in 3.6b4. -- --Guido van Rossum (python.org/~guido)
