Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions grpc_server_kit/aio/interceptors/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@
2. ``AsyncContextInterceptor`` (extract metadata into context vars)
3. ``AsyncRequestLoggerInterceptor`` (request/response logging)
4. ``AsyncTracingInterceptor`` (tracing span)
5. ``AsyncSentryInterceptor`` (error tracking)
6. ``AsyncExceptionHandlerInterceptor`` (exception → gRPC status mapping)
5. ``AsyncExceptionHandlerInterceptor`` (exception → gRPC status mapping)
6. ``AsyncSentryInterceptor`` (error tracking)

Sentry sits inside the exception handler so it sees the original exception;
outside, it would only see the ``grpc.aio.AbortError`` the handler turned it
into, and it never captures those.
"""

from .base import AsyncServerInterceptor, RpcCall, split_method_name
Expand Down
18 changes: 18 additions & 0 deletions tests/unit/aio/dishka/test_providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from __future__ import annotations

import re
from collections.abc import AsyncIterator, Awaitable, Callable
from unittest.mock import MagicMock, patch

Expand All @@ -10,6 +11,7 @@
from dishka import AsyncContainer, Provider, make_async_container
from dishka.integrations.grpcio import DishkaAioInterceptor

from grpc_server_kit.aio import interceptors as interceptors_module
from grpc_server_kit.aio.dishka import (
AsyncGrpcServerProvider,
GrpcServerInterceptorsProvider,
Expand Down Expand Up @@ -105,6 +107,22 @@ async def test__grpc_server_providers__default_bundle__wires_service_name_chain_
assert server.raw_server is not None


async def test__grpc_server_providers__default_bundle__matches_order_documented_in_interceptors_module(
container_factory: ContainerFactory,
make_settings: Callable[..., BaseGrpcServerSettings],
) -> None:
# Arrange
container = await container_factory(*grpc_server_providers(make_settings(), service_name=SERVICE))
documented = re.findall(r"^\d+\. ``(\w+)``", interceptors_module.__doc__ or "", flags=re.MULTILINE)

# Act
chain = await container.get(INTERCEPTORS_KEY)

# Assert
# The module docstring is the order users copy by hand; it must not drift from the provider.
assert documented == [type(i).__name__ for i in chain if not isinstance(i, DishkaAioInterceptor)]


async def test__grpc_server_interceptors_provider__sentry_and_tracing_disabled__drops_them_from_chain(
container_factory: ContainerFactory,
make_settings: Callable[..., BaseGrpcServerSettings],
Expand Down
Loading