Skip to content
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

Add per-task cancellation? #2

Open
arthur-tacca opened this issue Feb 5, 2024 · 1 comment
Open

Add per-task cancellation? #2

arthur-tacca opened this issue Feb 5, 2024 · 1 comment

Comments

@arthur-tacca
Copy link
Owner

I deliberately did not include a cancel scope with every ResultCapture object because cancel scopes are not free and, anyway, it goes against the spirit of the library: aioresult is intended for where you're happy with Trio's control flow, but just want to make a note of a return value. But I've realised it would be easy to add a cancel scope to a task by deriving a new class from ResultCapture, which would only be a few lines of code. It would even work with the start_soon() function because this is a class method.

Most importantly, it would be optional for callers, so it wouldn't burden those that don't need it, so I could be persuaded to add it to aioresult. Would it be of interest to anyone?

class CancellableCapture(ResultCapture):
    def __init__(self, routine, *args, suppress_exception: bool = False):
        super().__init__(routine, suppress_exception)
        self._cancel_scope = trio.CancelScope()

    async def run(self, **kwargs):
        with self._cancel_scope:
            await super().run(**kwargs)

    def cancel(self):
        self._cancel_scope.cancel()

    @property
    def cancel_scope(self):
        return self._cancel_scope

This does not attempt to deal with automatic cancellation where there are complex interdependencies between tasks (e.g. as discussed in python-trio/trio#892 and elsewhere) – that is definitely out of scope for this library. But at least it gives you a tool to help solve that manually at the application level.

One difficult aspect to the interface is what result() and exception() should return when a task has been cancelled. With the above implementation you would get the Cancelled exception (assuming that it successfully propagated all the way out of the routine). This is a bit odd because the exception would not be propagated to the containing nursery. The caller could check whether the exception was due to their own cancellation by inspecting my_capture.cancel_scope.cancelled_caught.

(An earlier draft of this code also optionally allowed you to pass in an existing cancel scope to the constructor, perhaps so that you could use the same cancel scope in multiple tasks. But then I remembered that cancel scopes are not reusable in Trio: see python-trio/trio#886.)

@davidbrochart
Copy link

I think it would be interesting. I suggested something similar in AnyIO, and Asphat v5.0 will have a TaskHandle that allows cancelling a single task.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants