-
-
Notifications
You must be signed in to change notification settings - Fork 178
Proposal: Rename ensure_future to create_task #477
Comments
To be honest, the most untuitive one was asyncio.async, but got deprecated for some reason. I like I know that it is now a proper keyword, but maybe we could do something sensible with the keyword? async def foo():
something
def main():
task = async foo() |
The reason is “async” becoming a keyword.
I don’t think this is a good idea. Making And |
If it's a lot of work, I can understand. But IMHO If I'm allowed just a bit more of bike-shedding, how about a function |
If we want to add a function that only accepts coroutines and turns them into tasks, I'd propose to call it
|
+1 to the name spawn.
I pretty much never use ensure_future because the name makes my code look
ambiguous and it has a small amount of overhead compared to
loop.create_task. I also don't like the soft edges and interpolation done
by ensure_future but that's another topic. My ideal world would be an
asyncio.spawn which is just asyncio.create_task. It would assert that the
argument was a coroutine in debug mode and that's it.
…On Mon, Dec 19, 2016 at 10:54 AM Yury Selivanov ***@***.***> wrote:
ensure_future can accept Futures and Tasks, in addition to coroutines. If
it's the latter, it will wrap it in a Task. If it's the former, it will
just return the passed object doing nothing. The current name clearly
reflects what the function is actually doing.
If we want to add a function that only accepts coroutines and turns them
into tasks, I'd propose to call it spawn.
start isn't bad, but we're considering
<#465> to add asyncio.run in the
near future, so I think we need more task-specific name.
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#477 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AAIgNK8fnVtFI-M0zQfpSe6DzuehoSUUks5rJsTJgaJpZM4LQ5lV>
.
|
Yes, If we add |
+1 for |
-1 on spawn(), we already have a family of os.spawn*() functions that
create subprocesses.
…-1 on start(), it's too generic (and doesn't do the same kind of thing as
Thread.start() at all).
-1 on create_task(), since if may not return a Task instance.
Andrew, was there a reason why you couldn't recommend your students to use
loop.create_task()?
|
Thanks for having this discussion. :) My question is: what's the key rationale to have both Another concern is that |
So, how about |
Oh, this is an interesting (to me) misunderstanding. The thing that is "ensured" is the type of the return value, not its eventual execution. The point of If you know that you have a coroutine and you want it to be scheduled, the correct API to use is Hopefully this helps. I am beginning to feel that the main thing that's wrong here is that the treatment in the docs is not very good... |
Thanks for clarification. So Still, I have doubt if it's a good name. The current naming is focusing on the input/output types rather than what it does with the input. And what it does seems to overlap with The docs need improvements as well, because several key example codes related to tasks are using
I'd like to know if there are technical reasons to use |
Guido, I understand you don't like It's a pity we cannot use |
Here's a counter-question. Why are you using these functions? My guess is that you've got a coroutine that you want to run "in the background", i.e. without waiting for it. What does that coroutine do? Is it a long-running server task? But why not have a "main" task that waits for all your server tasks (using Regarding the docs: https://docs.python.org/3.6/library/asyncio-task.html#example-parallel-execution-of-tasks -- This should not need the loop.run_complete(gather(
factorial("A", 2),
factorial("B", 3),
factorial("C", 4))) (But before you propose a patch please test this suggestion!) https://docs.python.org/3.6/library/asyncio-dev.html#chain-coroutines-correctly -- In the end I still believe that |
Yes, that's the idea. It would be nice to have a way to create tasks without explicit loop handling. So
The initial motivation for adding |
OK, I'm not totally against spawn() then. I think we should have a PEP
describing all the (major) improvements we want to make to asyncio in
Python 3.7 rather than doing it piecemeal -- it's easy to lose track of the
coherence of functionality and terminology.
Quite separately, we really should try to find someone who wants to improve
the docs. Both the reference docs and the tutorial material we have on
docs.python.org are obviously lacking, and my theory is that that has
happened because none of the original asyncio core devs (including myself)
felt like writing them. :-(
|
I think I'll make a first draft of the PEP soon. I'll document proposed
I already found such person! @appeltel, who helped me with documenting PEP 525 and 530 is willing to help me with asyncio docs. I'm going to start working with him (and whoever else is interested) when the holidays are over. We'll have a fork of CPython repo on github to work on the docs project. |
My use case for "spawning" tasks is mainly to run background tasks, such as heartbeat timers and event subscribers taking notifications from external network sources.
Another use case I can think of is to implement parallelized request-reply handlers in network applications, though usually libraries such as aiozmq.rpc and aiohttp do |
I think In my own experience trying to follow the docs and learn how asyncio works, the first thing I wanted to do was make a simple "hello world"-like coroutine and run it, ideally in the REPL. Being able to play with aysncio on the REPL and start/stop the event loop as shown below is helpful for a learner to easily experiment with running coroutines:
Once the learner has a sense of how to schedule tasks, the utility and behavior of Personally, I find the name
In any case, explaining to someone the meaning of |
But that example should be written without explicitly creating tasks at all: loop.run_until_complete(asyncio.gather(foo('Alice'), foo('Bob'), asyncio.sleep(5))) I suspect that overusing explicit tasks is probably a symptom of classical "thread" thinking, and I'd like us to carefully steer new users away from that paradigm. Previously @achimnol wrote:
That's also a dangerous line of thought, since It seems one of the problems we have here is that there are too many ways to introduce concurrency -- we have at least three API layers, represented by callbacks, tasks and coroutines. But callbacks should be avoided if at all possible (they are the low-level machinery used to implement the rest, and to provide interoperability with other frameworks). Coroutines (in the form of Of course, realistic networked applications occasionally need things like watchdog tasks that are best implemented using explicit tasks, but these are just a few of the many things that networked applications need in order to be production-ready and robust. There ought to be no need to introduce the lower-level primitives right away in a tutorial. It's quite possible that once we have an outline for a tutorial ready we'll come up with some tweaks to the high-level API (e.g. the |
I like your approach to introduce concurrent I/O with coroutines as a new paradigm. 👍
Yes, I understand that asyncio is not for computational parallelism but concurrent I/O. That's also why I choose to use asyncio because my apps mostly do I/O rather than calculations. I look forward to see the improved tutorial as well as more intuitive high-level APIs as you mentioned. I can also add my hands if there are something I could help. |
Though I agree with @gvanrossum's approach to write better tutorials and guide new users to the coroutine way of thinking including tweaks for synchronous high-level APIs proposed in #465, I'd like to mention that those do not resolve this issue. 🙄 Even occasionally we still need task-creation-followed-by-asynchronously-scheduling-it functionality, and |
Thanks for the explanation of the intended design philosophy, @gvanrossum. From the current documentation it is clear that underlying layer of callbacks should be transparent to most users, but I was left with the impression that the average and even beginning user should be concerning themselves with tasks.
So Then if this simple API is no longer sufficient, if I need a coroutine to schedule one or more awaitables in some manner other than simply awaiting on it, the concept of a Task can be introduced, along with If the above is correct then I guess my previous comment was a bit misinformed - I see that there is no real reason to discuss |
Yes, that's right. When using I expect that the trickiest bit to get to happen naturally may actually be termination. I noticed that curio has some sleight of hand around that too (and the tutorial spends a lot of time on this topic). |
@achimnol I am fine with adding But any name that might suggest that the newly created task automatically starts running is actually dangerous, because it doesn't -- at least not until you I realize that |
I just wanted to add that I was explaining asyncio to a couple of coworkers and I found that they were also confused by the name |
Happy new year to everybody (though it's a bit late)!
@gvanrossum Yes, because the task is a coroutine task, it will have chance to run after the coroutine that created the task yields the control to the event loop ( The main confusion point here around is the naming -- many people (including me) are too familiar with existing multi-threading terms. In this sense, we might need to rethink the name "task" more aggressively -- maybe change all occurrences of "tasks" in user-facing APIs and docs to "coroutines"? Still, |
Or what about renaming
Don't forget about people who use daemon threads as well and some people who use them use |
@AraHaan I have an objection for that. A queue inside the event loop is an implementation detail; it may not use queue. Something like @gvanrossum To be clear, I can also accept Just for question: I guess the reason that task and coroutine exist separately in asyncio is that a same coroutine may be executed using different event loop schedulers, e.g., curio instead of asyncio. In this sense, a task is a coroutine associated with an event loop scheduler. In other words, coroutine is a language-level term (like another form of functions) and task is a library-level term with a concrete execution context. Am I right? |
Tasks and coroutines are different! They should not be confused. A coroutine is simply a function defined with Please just stop this debate. We can add a top-level function |
Thanks for explanation. I too don't have any better idea to |
This is probably the wrong place to be asking about this -- but is there an analog to the |
During my training sessions I constantly forced to say:
Maybe function renaming (with keeping backward compatible alias) could smooth asyncio learning curve a bit?
The text was updated successfully, but these errors were encountered: