Skip to content

[grpcio] Fix AioRpcError.trailing_metadata and Metadata iteration#15899

Open
pboulos wants to merge 1 commit into
python:mainfrom
pboulos:pboulos/grpcio-aiorpcerror-trailing-metadata
Open

[grpcio] Fix AioRpcError.trailing_metadata and Metadata iteration#15899
pboulos wants to merge 1 commit into
python:mainfrom
pboulos:pboulos/grpcio-aiorpcerror-trailing-metadata

Conversation

@pboulos

@pboulos pboulos commented Jun 12, 2026

Copy link
Copy Markdown

I encountered a typing issue when using grpc.aio and figured I would write a PR to solve it at source.

Two related fixes to the async (grpc.aio) metadata types:

  1. AioRpcError.trailing_metadata() inherited the synchronous
    RpcError.trailing_metadata() -> tuple[_Metadatum, ...], but at runtime returns a
    grpc.aio.Metadata
    (_call.py).
    Added the override.

  2. grpc.aio.Metadata was modelled as a Mapping, but the runtime class is a
    Collection that iterates (key, value) tuples
    (_metadata.py).
    Re-based on Collection[_MetadatumType] with an item-yielding __iter__; keyed
    access (m[key], m.get(...)) is unchanged.

Together these let for key, value in err.trailing_metadata() type-check — it
previously errored with "_Metadatum is not iterable".

import grpc.aio

def reason(err: grpc.aio.AioRpcError) -> None:
    for key, value in err.trailing_metadata():  # before: error "_Metadatum is not iterable"
...
>>> import grpc
>>> from grpc.aio import Metadata, AioRpcError
>>> m = Metadata(("x-request-id", "abc"))
>>> [type(x).__name__ for x in m]            # iterates items, not keys
['tuple']
>>> err = AioRpcError(grpc.StatusCode.NOT_FOUND, Metadata(), m)
>>> type(err.trailing_metadata()).__name__   # not tuple[_Metadatum, ...]
'Metadata'

Includes a regression test. runtests.py --run-stubtest stubs/grpcio passes
(mypy, pyright incl. stricter, test cases, stubtest).

Please let me know if I've overlooked something in the contribution process.

@pboulos pboulos force-pushed the pboulos/grpcio-aiorpcerror-trailing-metadata branch from 2ce78ec to 576278d Compare June 12, 2026 01:53
@pboulos

pboulos commented Jun 12, 2026

Copy link
Copy Markdown
Author

I originally included a regression test, but I saw the comments made on other PRs around performance considerations of adding tests so I opted to remove it. See it below for reference:

# Regression test for the AioRpcError.trailing_metadata() override: it must
# return the async grpc.aio.Metadata (iterating as (key, value) tuples), not the
# synchronous tuple[_Metadatum, ...] inherited from grpc.RpcError.
def check_aio_rpc_error_trailing_metadata(error: grpc.aio.AioRpcError) -> None:
    assert_type(error.trailing_metadata(), grpc.aio.Metadata)
    for key, value in error.trailing_metadata():
        assert_type(key, str)
        assert_type(value, grpc.aio._MetadataValue)

[pre-commit.ci] auto fixes from pre-commit.com hooks
@pboulos pboulos force-pushed the pboulos/grpcio-aiorpcerror-trailing-metadata branch from 5b06964 to af81a5e Compare June 12, 2026 01:59
@github-actions

Copy link
Copy Markdown
Contributor

According to mypy_primer, this change has no effect on the checked open source code. 🤖🎉

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

Successfully merging this pull request may close these issues.

1 participant