> On Oct 30, 2016, at 2:42 PM, Guido van Rossum <gu...@python.org> wrote: > > On Sun, Oct 30, 2016 at 11:27 AM, Yury Selivanov <yseliva...@gmail.com> wrote: >> >>> On Oct 30, 2016, at 1:58 PM, Guido van Rossum <gu...@python.org> wrote: >>> >>> Regarding the documentation, I doubt we can get it in shape just by >>> filing issues. We should really just have a tutorial written by >>> someone with a good understanding of asyncio and writing skills. >> >> Yeah, I agree. I don’t want to promise anything, but this is something I >> wanted to do for a long time now. If no one tackles this in the next couple >> of months, I’ll try to start the process myself. >> >>> >>> I wonder how enlightening the chapter by A. Jesse Jiryu Davis here >>> really is for beginners? >>> http://aosabook.org/en/500L/a-web-crawler-with-asyncio-coroutines.html >>> (my name is on it because I wrote the original code -- Jesse wrote all >>> the text and rewrote the code several times over :-). >> >> I think it’s an excellent piece, but maybe it’s a tad more detailed than >> what we need for asyncio docs. IMO the opening page of asyncio docs should >> cover the following: >> >> 1. Why asyncio - one paragraph. >> 2. What is event loop - one p. >> 3. What is a coroutine - one p. >> 4. asyncio essentials: get_event_loop, run_until_complete, close, >> create_task, gather, open_connection, start_server. > > IMO this focuses on the loop too much. We should explain more on how > to use coroutines. > >> 5. Advanced topics: should I pass event loop explicitly? etc >> >> Speaking about APIs and passing working with the loop. One thing that people >> find attractive in curio is that it doesn’t really focus on the loop. You >> start your initial coroutine with the loop (called “kernel” in curio) and >> that’s it. > > Isn't the equivalent in asyncio just as easy? Start your initial > coroutine with asyncio.get_event_loop().run_until_complete() and > that's it.
You also should call loop.close(), which adds try..except around your loop.run_until_complete code. You also should await on loop.shutdown_asyncgens in 3.6. Those things add up. > > How does curio open a connection? How does it start serving? Asyncio > requires the loop for those. Is the loop implicit in curio? Well, in asyncio you can just “await asyncio.open_connection()” without passing a loop explicitly. And that’s how curio does this too - you always use coroutines to do *everything*. There is API to reliably get the loop that runs your current code (‘asyncio.get_event_loop’ should be fixed a little bit in my opinion, but we already had this discussion here: https://github.com/python/asyncio/pull/355). So in curio the loop is implicit, and I think it’s actually a good design decision. Because the “root” of your program is always a coroutine, and you can reliably get the current loop, you don’t need to pass it around explicitly. I think that even with the current asyncio design we should aim for the similar user experience. We already have “asyncio.open_connection” that doesn’t require the loop. We can add “asyncio.run_in_executor” (as opposed to “loop.run_in_executor") so that there is one less API that requires you to care about the loop. > >> What if we could do the following: >> >> 1. Provide an “asyncio.run(coro)” function to run your "main()” coroutine >> program and take care of everything loop-related for you. > > So that's just asyncio.get_event_loop().run_until_complete(coro)? I > worry that the shortness of the call will make people call this from > inside coroutines or callbacks. > >> “asyncio.run” should create the loop, run the coroutine, cleanup >> asynchronous generators and unfinished tasks, close the loop etc. > > I'd be less opposed if it was called asyncio.main(). That makes it > clear that you run this one, inside your own main. I’d be OK with “asyncio.main". > >> 2. There are some APIs that can’t be called without a loop: run_in_executor, >> sockets ops etc. What if we add “asyncio.run_in_background” coroutine that >> would get the event loop and call run_in_executor on it? > > Well in theory you could add asyncio.foo() calls for every loop > method. I'm not sure I'm excited about that. Nor about using different > names. Yeah, let’s forget about using different names — a bad idea. I’ve just looked through events.py. I think we may only need to add “asyncio.run_in_executor(func)”, and maybe “asyncio.create_task(coroutine)” functions. With that it should be possible to have almost the same “loopless” experience in asyncio as with curio: async def program(): await asyncio.run_in_executor(func) await asyncio.open_connection(…) task = asyncio.create_task(…) asyncio.main(program()) > >> 3. In 3.7 we can add a Socket-like object with asynchronous “recv”, “send”, >> etc coroutines. Turns out people want this, as it’s more convenient to use >> than loop.sock_sendall. Using streams is of course better, but sometimes >> you want to quickly make your existing program async without a full rewrite. > > I'd question *why* people want sockets. Just more familiar API I think. > Perhaps it's really just a > wrapper around a StreamReader/Writer pair? Can a curio socket wrap a > subprocess? A pipe? A tty device? Curio socket is a very thin wrapper on top of python socket and whatever non-blocking stuff python allows to do. The API is essentially the same, with all blocking methods made coroutines. I think people find Curio streams a bit more intuitive because they are just one object, not a pair of reader/writer. Yury