> 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 <
yseli...@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