Skip to content

Async client turns caller cancellation into ConnectError(CANCELED) #459

Open

Description

@stefanvanburen

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:

async with asyncio.timeout(0.1):
    await client.make_hat(Size(inches=10))

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions