-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Update delay_cancellation
to accept any awaitable
#12468
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Update `delay_cancellation` to accept any awaitable, rather than just `Deferred`s. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,6 +25,7 @@ | |
Awaitable, | ||
Callable, | ||
Collection, | ||
Coroutine, | ||
Dict, | ||
Generic, | ||
Hashable, | ||
|
@@ -701,27 +702,54 @@ def stop_cancellation(deferred: "defer.Deferred[T]") -> "defer.Deferred[T]": | |
return new_deferred | ||
|
||
|
||
def delay_cancellation(deferred: "defer.Deferred[T]") -> "defer.Deferred[T]": | ||
"""Delay cancellation of a `Deferred` until it resolves. | ||
@overload | ||
def delay_cancellation(awaitable: "defer.Deferred[T]") -> "defer.Deferred[T]": | ||
... | ||
|
||
|
||
@overload | ||
def delay_cancellation(awaitable: Coroutine[Any, Any, T]) -> "defer.Deferred[T]": | ||
... | ||
|
||
|
||
@overload | ||
def delay_cancellation(awaitable: Awaitable[T]) -> Awaitable[T]: | ||
... | ||
|
||
|
||
def delay_cancellation(awaitable: Awaitable[T]) -> Awaitable[T]: | ||
"""Delay cancellation of a coroutine or `Deferred` awaitable until it resolves. | ||
|
||
Has the same effect as `stop_cancellation`, but the returned `Deferred` will not | ||
resolve with a `CancelledError` until the original `Deferred` resolves. | ||
resolve with a `CancelledError` until the original awaitable resolves. | ||
|
||
Args: | ||
deferred: The `Deferred` to protect against cancellation. May optionally follow | ||
the Synapse logcontext rules. | ||
deferred: The coroutine or `Deferred` to protect against cancellation. May | ||
optionally follow the Synapse logcontext rules. | ||
|
||
Returns: | ||
A new `Deferred`, which will contain the result of the original `Deferred`. | ||
The new `Deferred` will not propagate cancellation through to the original. | ||
When cancelled, the new `Deferred` will wait until the original `Deferred` | ||
resolves before failing with a `CancelledError`. | ||
A new `Deferred`, which will contain the result of the original coroutine or | ||
`Deferred`. The new `Deferred` will not propagate cancellation through to the | ||
original coroutine or `Deferred`. | ||
|
||
The new `Deferred` will follow the Synapse logcontext rules if `deferred` | ||
When cancelled, the new `Deferred` will wait until the original coroutine or | ||
`Deferred` resolves before failing with a `CancelledError`. | ||
|
||
The new `Deferred` will follow the Synapse logcontext rules if `awaitable` | ||
follows the Synapse logcontext rules. Otherwise the new `Deferred` should be | ||
wrapped with `make_deferred_yieldable`. | ||
""" | ||
|
||
# First, convert the awaitable into a `Deferred`. | ||
if isinstance(awaitable, defer.Deferred): | ||
deferred = awaitable | ||
elif isinstance(awaitable, Coroutine): | ||
deferred = defer.ensureDeferred(awaitable) | ||
richvdh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
else: | ||
# We have no idea what to do with this awaitable. | ||
# Let the caller `await` it normally. | ||
return awaitable | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what sort of awaitables do we have that are neither coroutine objects nor Deferreds, outside of things returned by There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Apart from I'm not keen on raising, since I do think we should support arbitrary completed awaitables, ie. if I'll add a comment mentioning the types of awaitable we'd expect here. |
||
|
||
def handle_cancel(new_deferred: "defer.Deferred[T]") -> None: | ||
# before the new deferred is cancelled, we `pause` it to stop the cancellation | ||
# propagating. we then `unpause` it once the wrapped deferred completes, to | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I slightly wonder if this is reliable.
Possibly we want
asyncio.iscoroutine
which allows a wider range of types, and has some caching.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good idea.
Deferred.fromCoroutine
accepts anything thatasyncio.iscoroutine
identifies as a coroutine, so it looks safe to do.