You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Async client turns caller cancellation into ConnectError(CANCELED) #459
On asyncio, the async client catches CancelledError and raises ConnectError(Code.CANCELED) in its place, so anything that cancels a call from the outside never sees its own cancellation. e.g. against a server that never responds:
raises ConnectError(CANCELED) rather than TimeoutError. Same with anyio.fail_after (ConnectError instead of TimeoutError) and anyio.move_on_after (the ConnectError escapes the block instead of moving on), and a task that gets cancel()ed ends with an exception rather than as cancelled. The asyncio docs are pretty direct about this: "In almost all situations the exception must be re-raised." (asyncio.CancelledError)
With #458 the client lets trio.Cancelled through to its scope, so the same code now behaves differently depending on the loop. FWIW grpc.aio lets the cancellation through too (checked with grpcio 1.85.1: asyncio.timeout gives TimeoutError, and a cancelled task ends up cancelled()).
I think we ought to re-raise the CancelledError when the calling task is being cancelled (asyncio.current_task().cancelling() > 0), and keep mapping any other CancelledError (e.g. from a future the transport cancelled) to CANCELED. Interceptors would still see the call end as CANCELED in on_end, so otel spans wouldn't change.
This does change things for anyone catching ConnectError with Code.CANCELED after cancelling a call themselves, so wanted to raise it before putting up a PR. (Have a branch on top of #458 that does this; it's ~20 lines in the client, plus the conformance client's asyncio wrapper reporting CANCELED itself, as the trio one already does.)
Activity