-
-
Notifications
You must be signed in to change notification settings - Fork 345
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Returning a value from inside a nursery block behaves counterintuitively #1493
Comments
Is there a clear use-case for All I can think of is "the code is a bit shorter", and given the potential confusion here it really doesn't seem worth saving the line or two to dedent the relevant statements. If that's it, it seems like a useful lint rule! |
I can only really think of two situations where it'd make sense, both where you probably want to do something different anyway. First is a task receiving some sort of "quit" message and wanting to exit, but that should probably also cancel the scope first. So the return could probably just be replaced by a Maybe what would be something more elegant is something like an |
@TeamSpen210 There's a bunch of historical discussion in #658 that you might find relevant to this question. I would support an async @Zac-HD I think a lint rule against return in a nursery block is a very good idea. It should probably also forbid break and continue if they would affect a loop that is outside the nursery. |
We've updated |
Quick: what do you expect the result of this code to be?
In both cases, the nursery nested child task exits normally (returning a value) while some other task in the nursery exits with an exception (Cancelled, in this case). One might expect both cases to behave equivalently. But I get
42
fromcalculate_1()
andNone
fromcalculate_2()
. When the innermost context manager decides not to suppress the exception, the return value is lost so the exception can continue to propagate.One can construct other confusing cases along the same lines, such as:
cancel_scope.cancel()
, the background tasks keep running, maybe forever. Empirically users seem to be surprised by this -- they know that falling off the bottom of a nursery block waits to join the tasks, but the expectation is that areturn
would behave differently. Ditto forbreak
andcontinue
if the loop is outside the nursery.From
__aexit__
we can't directly tell the difference between a control flow transfer and a simple fall-off-the-end. (It might be possible with bytecode introspection, but IMO that's much too high a cost to incur on every nursery exit, and the patterns you'd need to recognize changed substantially in 3.8.) So our options here are somewhat limited.One option would be to not change anything in code, but just document this pitfall. Maybe even in a section of "common pitfalls" with some others we've seen.
The only other option I can think of right now would be to add a nursery method that can be called to indicate "I'm done with this nursery, I've completed the high-level task I was using it for, please don't interfere with me as I return that result". This might look something like: cancel all tasks in the nursery and filter out any Cancelled exceptions. One could imagine a stronger version that filters out all exceptions, though that's getting close to dangerous territory IMO.
Perhaps this should be a feature of "service nurseries" (as discussed in #147) rather than all nurseries, since it reinforces the idea that there's a difference between the nested child task and all the other tasks.
The text was updated successfully, but these errors were encountered: