@@ -295,3 +295,97 @@ async def _do_request(idx: int) -> int:
295295 assert e .args [0 ] == "ZeroDivisionError"
296296 else :
297297 assert call_results [idx ] == idx
298+
299+
300+ @pytest .mark .asyncio
301+ async def test_external_context_injection () -> None :
302+ """Test that an externally injected zmq context is used and not destroyed on close."""
303+ import zmq .asyncio
304+
305+ # Create an external context
306+ external_zctx = zmq .asyncio .Context ()
307+
308+ async def func (request : RPCMessage ) -> str :
309+ return "ok"
310+
311+ # Create server with external context
312+ server = Peer (
313+ bind = ZeroMQAddress ("tcp://127.0.0.1:5021" ),
314+ transport = ZeroMQRPCTransport ,
315+ transport_opts = {"zctx" : external_zctx },
316+ scheduler = ExitOrderedAsyncScheduler (),
317+ serializer = lambda o : json .dumps (o ).encode ("utf8" ),
318+ deserializer = lambda b : json .loads (b ),
319+ )
320+ server .handle_function ("func" , func )
321+
322+ # Create client with external context
323+ client = Peer (
324+ connect = ZeroMQAddress ("tcp://localhost:5021" ),
325+ transport = ZeroMQRPCTransport ,
326+ transport_opts = {"zctx" : external_zctx },
327+ serializer = lambda o : json .dumps (o ).encode ("utf8" ),
328+ deserializer = lambda b : json .loads (b ),
329+ )
330+
331+ # Verify the external context is used
332+ server_transport = cast (ZeroMQRPCTransport , server ._transport )
333+ client_transport = cast (ZeroMQRPCTransport , client ._transport )
334+ assert server_transport ._zctx is external_zctx
335+ assert server_transport ._external_zctx is True
336+ assert client_transport ._zctx is external_zctx
337+ assert client_transport ._external_zctx is True
338+
339+ async with server :
340+ async with client :
341+ result = await client .invoke ("func" , {})
342+ assert result == "ok"
343+
344+ # Verify context is NOT destroyed after transport close
345+ assert not external_zctx .closed
346+
347+ # Clean up
348+ external_zctx .destroy (linger = 0 )
349+
350+
351+ @pytest .mark .asyncio
352+ async def test_internal_context_destroyed_on_close () -> None :
353+ """Test that internally created zmq context is destroyed on close."""
354+
355+ async def func (request : RPCMessage ) -> str :
356+ return "ok"
357+
358+ # Create server without external context (uses internal)
359+ server = Peer (
360+ bind = ZeroMQAddress ("tcp://127.0.0.1:5022" ),
361+ transport = ZeroMQRPCTransport ,
362+ scheduler = ExitOrderedAsyncScheduler (),
363+ serializer = lambda o : json .dumps (o ).encode ("utf8" ),
364+ deserializer = lambda b : json .loads (b ),
365+ )
366+ server .handle_function ("func" , func )
367+
368+ # Create client without external context (uses internal)
369+ client = Peer (
370+ connect = ZeroMQAddress ("tcp://localhost:5022" ),
371+ transport = ZeroMQRPCTransport ,
372+ serializer = lambda o : json .dumps (o ).encode ("utf8" ),
373+ deserializer = lambda b : json .loads (b ),
374+ )
375+
376+ # Verify internal context is created
377+ server_transport = cast (ZeroMQRPCTransport , server ._transport )
378+ client_transport = cast (ZeroMQRPCTransport , client ._transport )
379+ assert server_transport ._external_zctx is False
380+ assert client_transport ._external_zctx is False
381+ server_zctx = server_transport ._zctx
382+ client_zctx = client_transport ._zctx
383+
384+ async with server :
385+ async with client :
386+ result = await client .invoke ("func" , {})
387+ assert result == "ok"
388+
389+ # Verify context IS destroyed after transport close
390+ assert server_zctx .closed
391+ assert client_zctx .closed
0 commit comments