From fe02e998487cdd8112589efc46391e2f993b1b53 Mon Sep 17 00:00:00 2001 From: Anuraag Agrawal Date: Tue, 3 Mar 2026 11:21:39 +0900 Subject: [PATCH 1/2] Pass exception to metadata interceptors Signed-off-by: Anuraag Agrawal --- src/connectrpc/_interceptor_async.py | 28 ++++- src/connectrpc/_interceptor_sync.py | 28 ++++- test/test_interceptor.py | 163 ++++++++++++++++++++++++++- 3 files changed, 205 insertions(+), 14 deletions(-) diff --git a/src/connectrpc/_interceptor_async.py b/src/connectrpc/_interceptor_async.py index 5d8e372..e641c73 100644 --- a/src/connectrpc/_interceptor_async.py +++ b/src/connectrpc/_interceptor_async.py @@ -136,7 +136,9 @@ async def on_start(self, ctx: RequestContext) -> T: """ ... - async def on_end(self, token: T, ctx: RequestContext) -> None: + async def on_end( + self, token: T, ctx: RequestContext, error: Exception | None + ) -> None: """Called when the RPC ends.""" return @@ -164,10 +166,14 @@ async def intercept_unary( ctx: RequestContext, ) -> RES: token = await self._delegate.on_start(ctx) + error: Exception | None = None try: return await call_next(request, ctx) + except Exception as e: + error = e + raise finally: - await self._delegate.on_end(token, ctx) + await self._delegate.on_end(token, ctx, error) async def intercept_client_stream( self, @@ -176,10 +182,14 @@ async def intercept_client_stream( ctx: RequestContext, ) -> RES: token = await self._delegate.on_start(ctx) + error: Exception | None = None try: return await call_next(request, ctx) + except Exception as e: + error = e + raise finally: - await self._delegate.on_end(token, ctx) + await self._delegate.on_end(token, ctx, error) async def intercept_server_stream( self, @@ -188,11 +198,15 @@ async def intercept_server_stream( ctx: RequestContext, ) -> AsyncIterator[RES]: token = await self._delegate.on_start(ctx) + error: Exception | None = None try: async for response in call_next(request, ctx): yield response + except Exception as e: + error = e + raise finally: - await self._delegate.on_end(token, ctx) + await self._delegate.on_end(token, ctx, error) async def intercept_bidi_stream( self, @@ -201,11 +215,15 @@ async def intercept_bidi_stream( ctx: RequestContext, ) -> AsyncIterator[RES]: token = await self._delegate.on_start(ctx) + error: Exception | None = None try: async for response in call_next(request, ctx): yield response + except Exception as e: + error = e + raise finally: - await self._delegate.on_end(token, ctx) + await self._delegate.on_end(token, ctx, error) def resolve_interceptors( diff --git a/src/connectrpc/_interceptor_sync.py b/src/connectrpc/_interceptor_sync.py index 7b0ca8a..ad86229 100644 --- a/src/connectrpc/_interceptor_sync.py +++ b/src/connectrpc/_interceptor_sync.py @@ -136,7 +136,9 @@ def on_start_sync(self, ctx: RequestContext) -> T: """ ... - def on_end_sync(self, token: T, ctx: RequestContext) -> None: + def on_end_sync( + self, token: T, ctx: RequestContext, error: Exception | None + ) -> None: """Called when the RPC ends.""" return @@ -164,10 +166,14 @@ def intercept_unary_sync( ctx: RequestContext, ) -> RES: token = self._delegate.on_start_sync(ctx) + error: Exception | None = None try: return call_next(request, ctx) + except Exception as e: + error = e + raise finally: - self._delegate.on_end_sync(token, ctx) + self._delegate.on_end_sync(token, ctx, error) def intercept_client_stream_sync( self, @@ -176,10 +182,14 @@ def intercept_client_stream_sync( ctx: RequestContext, ) -> RES: token = self._delegate.on_start_sync(ctx) + error: Exception | None = None try: return call_next(request, ctx) + except Exception as e: + error = e + raise finally: - self._delegate.on_end_sync(token, ctx) + self._delegate.on_end_sync(token, ctx, error) def intercept_server_stream_sync( self, @@ -188,10 +198,14 @@ def intercept_server_stream_sync( ctx: RequestContext, ) -> Iterator[RES]: token = self._delegate.on_start_sync(ctx) + error: Exception | None = None try: yield from call_next(request, ctx) + except Exception as e: + error = e + raise finally: - self._delegate.on_end_sync(token, ctx) + self._delegate.on_end_sync(token, ctx, error) def intercept_bidi_stream_sync( self, @@ -200,10 +214,14 @@ def intercept_bidi_stream_sync( ctx: RequestContext, ) -> Iterator[RES]: token = self._delegate.on_start_sync(ctx) + error: Exception | None = None try: yield from call_next(request, ctx) + except Exception as e: + error = e + raise finally: - self._delegate.on_end_sync(token, ctx) + self._delegate.on_end_sync(token, ctx, error) def resolve_interceptors( diff --git a/test/test_interceptor.py b/test/test_interceptor.py index e08633f..1539b73 100644 --- a/test/test_interceptor.py +++ b/test/test_interceptor.py @@ -8,6 +8,9 @@ from pyqwest import Client, SyncClient from pyqwest.testing import ASGITransport, WSGITransport +from connectrpc.code import Code +from connectrpc.errors import ConnectError + from .haberdasher_connect import ( Haberdasher, HaberdasherASGIApplication, @@ -29,14 +32,21 @@ def __init__(self) -> None: async def on_start(self, ctx: RequestContext): return self.on_start_sync(ctx) - async def on_end(self, token: str, ctx: RequestContext) -> None: - self.on_end_sync(token, ctx) + async def on_end( + self, token: str, ctx: RequestContext, error: Exception | None + ) -> None: + self.on_end_sync(token, ctx, error) def on_start_sync(self, ctx: RequestContext) -> str: return f"Hello {ctx.method().name}" - def on_end_sync(self, token: str, ctx: RequestContext) -> None: - self.result.append(f"{token} and goodbye") + def on_end_sync( + self, token: str, ctx: RequestContext, error: Exception | None + ) -> None: + msg = f"{token} and goodbye" + if error is not None: + msg += f" with error {error}" + self.result.append(msg) @pytest.fixture @@ -48,21 +58,33 @@ def interceptor(): async def client_async(interceptor: RequestInterceptor): class SimpleHaberdasher(Haberdasher): async def make_hat(self, request, ctx): + if request.inches < 0: + raise ConnectError(Code.INVALID_ARGUMENT, "Size must be non-negative") return Hat(size=request.inches, color="green") async def make_flexible_hat(self, request, ctx): size = 0 async for s in request: + if s.inches < 0: + raise ConnectError( + Code.INVALID_ARGUMENT, "Size must be non-negative" + ) size += s.inches return Hat(size=size, color="red") async def make_similar_hats(self, request, ctx): + if request.inches < 0: + raise ConnectError(Code.INVALID_ARGUMENT, "Size must be non-negative") yield Hat(size=request.inches, color="orange") yield Hat(size=request.inches, color="blue") async def make_various_hats(self, request, ctx): colors = itertools.cycle(("black", "white", "gold")) async for s in request: + if s.inches < 0: + raise ConnectError( + Code.INVALID_ARGUMENT, "Size must be non-negative" + ) yield Hat(size=s.inches, color=next(colors)) app = HaberdasherASGIApplication(SimpleHaberdasher(), interceptors=(interceptor,)) @@ -84,6 +106,18 @@ async def test_intercept_unary_async( assert interceptor.result == ["Hello MakeHat and goodbye"] * 2 +@pytest.mark.asyncio +async def test_intercept_unary_async_error( + client_async: HaberdasherClient, interceptor: RequestInterceptor +) -> None: + with pytest.raises(ConnectError): + await client_async.make_hat(Size(inches=-10)) + assert ( + interceptor.result + == ["Hello MakeHat and goodbye with error Size must be non-negative"] * 2 + ) + + @pytest.mark.asyncio async def test_intercept_client_stream_async( client_async: HaberdasherClient, interceptor: RequestInterceptor @@ -97,6 +131,23 @@ async def requests(): assert interceptor.result == ["Hello MakeFlexibleHat and goodbye"] * 2 +@pytest.mark.asyncio +async def test_intercept_client_stream_async_error( + client_async: HaberdasherClient, interceptor: RequestInterceptor +) -> None: + async def requests(): + yield Size(inches=-10) + yield Size(inches=20) + + with pytest.raises(ConnectError): + await client_async.make_flexible_hat(requests()) + assert ( + interceptor.result + == ["Hello MakeFlexibleHat and goodbye with error Size must be non-negative"] + * 2 + ) + + @pytest.mark.asyncio async def test_intercept_server_stream_async( client_async: HaberdasherClient, interceptor: RequestInterceptor @@ -107,6 +158,21 @@ async def test_intercept_server_stream_async( assert interceptor.result == ["Hello MakeSimilarHats and goodbye"] * 2 +@pytest.mark.asyncio +async def test_intercept_server_stream_async_error( + client_async: HaberdasherClient, interceptor: RequestInterceptor +) -> None: + with pytest.raises(ConnectError): + async for _ in client_async.make_similar_hats(Size(inches=-15)): + pass + + assert ( + interceptor.result + == ["Hello MakeSimilarHats and goodbye with error Size must be non-negative"] + * 2 + ) + + @pytest.mark.asyncio async def test_intercept_bidi_stream_async( client_async: HaberdasherClient, interceptor: RequestInterceptor @@ -126,19 +192,47 @@ async def requests(): assert interceptor.result == ["Hello MakeVariousHats and goodbye"] * 2 +@pytest.mark.asyncio +async def test_intercept_bidi_stream_async_error( + client_async: HaberdasherClient, interceptor: RequestInterceptor +) -> None: + async def requests(): + yield Size(inches=-25) + yield Size(inches=35) + yield Size(inches=45) + + with pytest.raises(ConnectError): + async for _ in client_async.make_various_hats(requests()): + pass + + assert ( + interceptor.result + == ["Hello MakeVariousHats and goodbye with error Size must be non-negative"] + * 2 + ) + + @pytest.fixture def client_sync(interceptor: RequestInterceptor): class SimpleHaberdasherSync(HaberdasherSync): def make_hat(self, request, ctx): + if request.inches < 0: + raise ConnectError(Code.INVALID_ARGUMENT, "Size must be non-negative") return Hat(size=request.inches, color="green") def make_flexible_hat(self, request, ctx): size = 0 for s in request: + if s.inches < 0: + raise ConnectError( + Code.INVALID_ARGUMENT, "Size must be non-negative" + ) size += s.inches return Hat(size=size, color="red") def make_similar_hats(self, request, ctx): + if request.inches < 0: + raise ConnectError(Code.INVALID_ARGUMENT, "Size must be non-negative") yield Hat(size=request.inches, color="orange") yield Hat(size=request.inches, color="blue") @@ -146,6 +240,10 @@ def make_various_hats(self, request, ctx): colors = itertools.cycle(("black", "white", "gold")) requests = [*request] for s in requests: + if s.inches < 0: + raise ConnectError( + Code.INVALID_ARGUMENT, "Size must be non-negative" + ) yield Hat(size=s.inches, color=next(colors)) app = HaberdasherWSGIApplication( @@ -168,6 +266,17 @@ def test_intercept_unary_sync( assert interceptor.result == ["Hello MakeHat and goodbye"] * 2 +def test_intercept_unary_sync_error( + client_sync: HaberdasherClientSync, interceptor: RequestInterceptor +) -> None: + with pytest.raises(ConnectError): + client_sync.make_hat(Size(inches=-10)) + assert ( + interceptor.result + == ["Hello MakeHat and goodbye with error Size must be non-negative"] * 2 + ) + + def test_intercept_client_stream_sync( client_sync: HaberdasherClientSync, interceptor: RequestInterceptor ) -> None: @@ -180,6 +289,22 @@ def requests(): assert interceptor.result == ["Hello MakeFlexibleHat and goodbye"] * 2 +def test_intercept_client_stream_sync_error( + client_sync: HaberdasherClientSync, interceptor: RequestInterceptor +) -> None: + def requests(): + yield Size(inches=-10) + yield Size(inches=20) + + with pytest.raises(ConnectError): + client_sync.make_flexible_hat(requests()) + assert ( + interceptor.result + == ["Hello MakeFlexibleHat and goodbye with error Size must be non-negative"] + * 2 + ) + + def test_intercept_server_stream_sync( client_sync: HaberdasherClientSync, interceptor: RequestInterceptor ) -> None: @@ -189,6 +314,18 @@ def test_intercept_server_stream_sync( assert interceptor.result == ["Hello MakeSimilarHats and goodbye"] * 2 +def test_intercept_server_stream_sync_error( + client_sync: HaberdasherClientSync, interceptor: RequestInterceptor +) -> None: + with pytest.raises(ConnectError): + list(client_sync.make_similar_hats(Size(inches=-15))) + assert ( + interceptor.result + == ["Hello MakeSimilarHats and goodbye with error Size must be non-negative"] + * 2 + ) + + def test_intercept_bidi_stream_sync( client_sync: HaberdasherClientSync, interceptor: RequestInterceptor ) -> None: @@ -205,3 +342,21 @@ def requests(): Hat(size=45, color="gold"), ] assert interceptor.result == ["Hello MakeVariousHats and goodbye"] * 2 + + +def test_intercept_bidi_stream_sync_error( + client_sync: HaberdasherClientSync, interceptor: RequestInterceptor +) -> None: + def requests(): + yield Size(inches=-25) + yield Size(inches=35) + yield Size(inches=45) + + with pytest.raises(ConnectError): + list(client_sync.make_various_hats(requests())) + + assert ( + interceptor.result + == ["Hello MakeVariousHats and goodbye with error Size must be non-negative"] + * 2 + ) From ac957db507bf4089a7b6c633c0e651eb31a3e940 Mon Sep 17 00:00:00 2001 From: Anuraag Agrawal Date: Tue, 3 Mar 2026 23:43:20 +0900 Subject: [PATCH 2/2] Cleanup Signed-off-by: Anuraag Agrawal --- test/test_interceptor.py | 199 +++++++++++++++++++++++++-------------- 1 file changed, 130 insertions(+), 69 deletions(-) diff --git a/test/test_interceptor.py b/test/test_interceptor.py index 1539b73..548d7d7 100644 --- a/test/test_interceptor.py +++ b/test/test_interceptor.py @@ -50,12 +50,19 @@ def on_end_sync( @pytest.fixture -def interceptor(): +def client_interceptor(): + return RequestInterceptor() + + +@pytest.fixture +def server_interceptor(): return RequestInterceptor() @pytest_asyncio.fixture -async def client_async(interceptor: RequestInterceptor): +async def client_async( + client_interceptor: RequestInterceptor, server_interceptor: RequestInterceptor +): class SimpleHaberdasher(Haberdasher): async def make_hat(self, request, ctx): if request.inches < 0: @@ -87,11 +94,13 @@ async def make_various_hats(self, request, ctx): ) yield Hat(size=s.inches, color=next(colors)) - app = HaberdasherASGIApplication(SimpleHaberdasher(), interceptors=(interceptor,)) + app = HaberdasherASGIApplication( + SimpleHaberdasher(), interceptors=(server_interceptor,) + ) transport = ASGITransport(app) async with HaberdasherClient( "http://localhost", - interceptors=(interceptor,), + interceptors=(client_interceptor,), http_client=Client(transport=transport), ) as client: yield client @@ -99,28 +108,37 @@ async def make_various_hats(self, request, ctx): @pytest.mark.asyncio async def test_intercept_unary_async( - client_async: HaberdasherClient, interceptor: RequestInterceptor + client_async: HaberdasherClient, + client_interceptor: RequestInterceptor, + server_interceptor: RequestInterceptor, ) -> None: result = await client_async.make_hat(Size(inches=10)) assert result == Hat(size=10, color="green") - assert interceptor.result == ["Hello MakeHat and goodbye"] * 2 + assert client_interceptor.result == ["Hello MakeHat and goodbye"] + assert server_interceptor.result == ["Hello MakeHat and goodbye"] @pytest.mark.asyncio async def test_intercept_unary_async_error( - client_async: HaberdasherClient, interceptor: RequestInterceptor + client_async: HaberdasherClient, + client_interceptor: RequestInterceptor, + server_interceptor: RequestInterceptor, ) -> None: with pytest.raises(ConnectError): await client_async.make_hat(Size(inches=-10)) - assert ( - interceptor.result - == ["Hello MakeHat and goodbye with error Size must be non-negative"] * 2 - ) + assert client_interceptor.result == [ + "Hello MakeHat and goodbye with error Size must be non-negative" + ] + assert server_interceptor.result == [ + "Hello MakeHat and goodbye with error Size must be non-negative" + ] @pytest.mark.asyncio async def test_intercept_client_stream_async( - client_async: HaberdasherClient, interceptor: RequestInterceptor + client_async: HaberdasherClient, + client_interceptor: RequestInterceptor, + server_interceptor: RequestInterceptor, ) -> None: async def requests(): yield Size(inches=10) @@ -128,12 +146,15 @@ async def requests(): result = await client_async.make_flexible_hat(requests()) assert result == Hat(size=30, color="red") - assert interceptor.result == ["Hello MakeFlexibleHat and goodbye"] * 2 + assert client_interceptor.result == ["Hello MakeFlexibleHat and goodbye"] + assert server_interceptor.result == ["Hello MakeFlexibleHat and goodbye"] @pytest.mark.asyncio async def test_intercept_client_stream_async_error( - client_async: HaberdasherClient, interceptor: RequestInterceptor + client_async: HaberdasherClient, + client_interceptor: RequestInterceptor, + server_interceptor: RequestInterceptor, ) -> None: async def requests(): yield Size(inches=-10) @@ -141,41 +162,50 @@ async def requests(): with pytest.raises(ConnectError): await client_async.make_flexible_hat(requests()) - assert ( - interceptor.result - == ["Hello MakeFlexibleHat and goodbye with error Size must be non-negative"] - * 2 - ) + assert client_interceptor.result == [ + "Hello MakeFlexibleHat and goodbye with error Size must be non-negative" + ] + assert server_interceptor.result == [ + "Hello MakeFlexibleHat and goodbye with error Size must be non-negative" + ] @pytest.mark.asyncio async def test_intercept_server_stream_async( - client_async: HaberdasherClient, interceptor: RequestInterceptor + client_async: HaberdasherClient, + client_interceptor: RequestInterceptor, + server_interceptor: RequestInterceptor, ) -> None: result = [r async for r in client_async.make_similar_hats(Size(inches=15))] assert result == [Hat(size=15, color="orange"), Hat(size=15, color="blue")] - assert interceptor.result == ["Hello MakeSimilarHats and goodbye"] * 2 + assert client_interceptor.result == ["Hello MakeSimilarHats and goodbye"] + assert server_interceptor.result == ["Hello MakeSimilarHats and goodbye"] @pytest.mark.asyncio async def test_intercept_server_stream_async_error( - client_async: HaberdasherClient, interceptor: RequestInterceptor + client_async: HaberdasherClient, + client_interceptor: RequestInterceptor, + server_interceptor: RequestInterceptor, ) -> None: with pytest.raises(ConnectError): async for _ in client_async.make_similar_hats(Size(inches=-15)): pass - assert ( - interceptor.result - == ["Hello MakeSimilarHats and goodbye with error Size must be non-negative"] - * 2 - ) + assert client_interceptor.result == [ + "Hello MakeSimilarHats and goodbye with error Size must be non-negative" + ] + assert server_interceptor.result == [ + "Hello MakeSimilarHats and goodbye with error Size must be non-negative" + ] @pytest.mark.asyncio async def test_intercept_bidi_stream_async( - client_async: HaberdasherClient, interceptor: RequestInterceptor + client_async: HaberdasherClient, + client_interceptor: RequestInterceptor, + server_interceptor: RequestInterceptor, ) -> None: async def requests(): yield Size(inches=25) @@ -189,12 +219,15 @@ async def requests(): Hat(size=35, color="white"), Hat(size=45, color="gold"), ] - assert interceptor.result == ["Hello MakeVariousHats and goodbye"] * 2 + assert client_interceptor.result == ["Hello MakeVariousHats and goodbye"] + assert server_interceptor.result == ["Hello MakeVariousHats and goodbye"] @pytest.mark.asyncio async def test_intercept_bidi_stream_async_error( - client_async: HaberdasherClient, interceptor: RequestInterceptor + client_async: HaberdasherClient, + client_interceptor: RequestInterceptor, + server_interceptor: RequestInterceptor, ) -> None: async def requests(): yield Size(inches=-25) @@ -205,15 +238,18 @@ async def requests(): async for _ in client_async.make_various_hats(requests()): pass - assert ( - interceptor.result - == ["Hello MakeVariousHats and goodbye with error Size must be non-negative"] - * 2 - ) + assert client_interceptor.result == [ + "Hello MakeVariousHats and goodbye with error Size must be non-negative" + ] + assert server_interceptor.result == [ + "Hello MakeVariousHats and goodbye with error Size must be non-negative" + ] @pytest.fixture -def client_sync(interceptor: RequestInterceptor): +def client_sync( + client_interceptor: RequestInterceptor, server_interceptor: RequestInterceptor +): class SimpleHaberdasherSync(HaberdasherSync): def make_hat(self, request, ctx): if request.inches < 0: @@ -247,38 +283,47 @@ def make_various_hats(self, request, ctx): yield Hat(size=s.inches, color=next(colors)) app = HaberdasherWSGIApplication( - SimpleHaberdasherSync(), interceptors=(interceptor,) + SimpleHaberdasherSync(), interceptors=(server_interceptor,) ) transport = WSGITransport(app) with HaberdasherClientSync( "http://localhost", - interceptors=(interceptor,), + interceptors=(client_interceptor,), http_client=SyncClient(transport), ) as client: yield client def test_intercept_unary_sync( - client_sync: HaberdasherClientSync, interceptor: RequestInterceptor + client_sync: HaberdasherClientSync, + client_interceptor: RequestInterceptor, + server_interceptor: RequestInterceptor, ) -> None: result = client_sync.make_hat(Size(inches=10)) assert result == Hat(size=10, color="green") - assert interceptor.result == ["Hello MakeHat and goodbye"] * 2 + assert client_interceptor.result == ["Hello MakeHat and goodbye"] + assert server_interceptor.result == ["Hello MakeHat and goodbye"] def test_intercept_unary_sync_error( - client_sync: HaberdasherClientSync, interceptor: RequestInterceptor + client_sync: HaberdasherClientSync, + client_interceptor: RequestInterceptor, + server_interceptor: RequestInterceptor, ) -> None: with pytest.raises(ConnectError): client_sync.make_hat(Size(inches=-10)) - assert ( - interceptor.result - == ["Hello MakeHat and goodbye with error Size must be non-negative"] * 2 - ) + assert client_interceptor.result == [ + "Hello MakeHat and goodbye with error Size must be non-negative" + ] + assert server_interceptor.result == [ + "Hello MakeHat and goodbye with error Size must be non-negative" + ] def test_intercept_client_stream_sync( - client_sync: HaberdasherClientSync, interceptor: RequestInterceptor + client_sync: HaberdasherClientSync, + client_interceptor: RequestInterceptor, + server_interceptor: RequestInterceptor, ) -> None: def requests(): yield Size(inches=10) @@ -286,11 +331,14 @@ def requests(): result = client_sync.make_flexible_hat(requests()) assert result == Hat(size=30, color="red") - assert interceptor.result == ["Hello MakeFlexibleHat and goodbye"] * 2 + assert client_interceptor.result == ["Hello MakeFlexibleHat and goodbye"] + assert server_interceptor.result == ["Hello MakeFlexibleHat and goodbye"] def test_intercept_client_stream_sync_error( - client_sync: HaberdasherClientSync, interceptor: RequestInterceptor + client_sync: HaberdasherClientSync, + client_interceptor: RequestInterceptor, + server_interceptor: RequestInterceptor, ) -> None: def requests(): yield Size(inches=-10) @@ -298,36 +346,45 @@ def requests(): with pytest.raises(ConnectError): client_sync.make_flexible_hat(requests()) - assert ( - interceptor.result - == ["Hello MakeFlexibleHat and goodbye with error Size must be non-negative"] - * 2 - ) + assert client_interceptor.result == [ + "Hello MakeFlexibleHat and goodbye with error Size must be non-negative" + ] + assert server_interceptor.result == [ + "Hello MakeFlexibleHat and goodbye with error Size must be non-negative" + ] def test_intercept_server_stream_sync( - client_sync: HaberdasherClientSync, interceptor: RequestInterceptor + client_sync: HaberdasherClientSync, + client_interceptor: RequestInterceptor, + server_interceptor: RequestInterceptor, ) -> None: result = list(client_sync.make_similar_hats(Size(inches=15))) assert result == [Hat(size=15, color="orange"), Hat(size=15, color="blue")] - assert interceptor.result == ["Hello MakeSimilarHats and goodbye"] * 2 + assert client_interceptor.result == ["Hello MakeSimilarHats and goodbye"] + assert server_interceptor.result == ["Hello MakeSimilarHats and goodbye"] def test_intercept_server_stream_sync_error( - client_sync: HaberdasherClientSync, interceptor: RequestInterceptor + client_sync: HaberdasherClientSync, + client_interceptor: RequestInterceptor, + server_interceptor: RequestInterceptor, ) -> None: with pytest.raises(ConnectError): list(client_sync.make_similar_hats(Size(inches=-15))) - assert ( - interceptor.result - == ["Hello MakeSimilarHats and goodbye with error Size must be non-negative"] - * 2 - ) + assert client_interceptor.result == [ + "Hello MakeSimilarHats and goodbye with error Size must be non-negative" + ] + assert server_interceptor.result == [ + "Hello MakeSimilarHats and goodbye with error Size must be non-negative" + ] def test_intercept_bidi_stream_sync( - client_sync: HaberdasherClientSync, interceptor: RequestInterceptor + client_sync: HaberdasherClientSync, + client_interceptor: RequestInterceptor, + server_interceptor: RequestInterceptor, ) -> None: def requests(): yield Size(inches=25) @@ -341,11 +398,14 @@ def requests(): Hat(size=35, color="white"), Hat(size=45, color="gold"), ] - assert interceptor.result == ["Hello MakeVariousHats and goodbye"] * 2 + assert client_interceptor.result == ["Hello MakeVariousHats and goodbye"] + assert server_interceptor.result == ["Hello MakeVariousHats and goodbye"] def test_intercept_bidi_stream_sync_error( - client_sync: HaberdasherClientSync, interceptor: RequestInterceptor + client_sync: HaberdasherClientSync, + client_interceptor: RequestInterceptor, + server_interceptor: RequestInterceptor, ) -> None: def requests(): yield Size(inches=-25) @@ -355,8 +415,9 @@ def requests(): with pytest.raises(ConnectError): list(client_sync.make_various_hats(requests())) - assert ( - interceptor.result - == ["Hello MakeVariousHats and goodbye with error Size must be non-negative"] - * 2 - ) + assert client_interceptor.result == [ + "Hello MakeVariousHats and goodbye with error Size must be non-negative" + ] + assert server_interceptor.result == [ + "Hello MakeVariousHats and goodbye with error Size must be non-negative" + ]