99
1010The fix snapshots the recorded warnings and logs them after leaving the
1111``catch_warnings`` block, so handler-emitted warnings can no longer extend the
12- iteration. This test drives ``_handle_message`` with a request that records one
13- warning and a logging handler that itself warns on every record; the handler
14- must be invoked exactly once. The handler stops re-warning after a cap so that
15- the pre-fix infinite loop terminates and fails the assertion instead of hanging
16- the test session.
12+ iteration.
13+
14+ The test records two warnings during handling and attaches a logging handler
15+ that itself warns on the first record only. After the fix each recorded warning
16+ is logged exactly once, so the handler is invoked twice (once per recorded
17+ warning) and no extra records appear. Before the fix, the handler's warning fed
18+ back into the iterated list and the handler was invoked a third time. Warning
19+ on only the first record keeps the handler from feeding the loop unboundedly, so
20+ a regressed build terminates and fails the assertion instead of hanging.
1721"""
1822
1923import logging
2933
3034
3135class _WarningEmittingHandler (logging .Handler ):
32- """A logging handler whose emit() raises a warning, mimicking e.g. a
33- timestamp formatter that calls a deprecated API on every record. It stops
34- after ``cap`` records so a regressed (looping) build still terminates."""
36+ """A logging handler whose emit() raises a warning for the first ``cap``
37+ records, mimicking e.g. a timestamp formatter that warns per record."""
3538
36- def __init__ (self , cap : int = 100 ) -> None :
39+ def __init__ (self , cap : int = 1 ) -> None :
3740 super ().__init__ ()
3841 self .emit_count = 0
3942 self ._cap = cap
@@ -45,14 +48,15 @@ def emit(self, record: logging.LogRecord) -> None:
4548
4649
4750@pytest .mark .anyio
48- async def test_handle_message_logs_each_warning_once_when_handler_warns () :
51+ async def test_handle_message_logs_each_recorded_warning_once () -> None :
4952 server = Server ("test-server" )
5053
5154 session = Mock (spec = ServerSession )
5255 session .send_log_message = AsyncMock ()
5356
5457 async def _handle_request_that_warns (* args : object , ** kwargs : object ) -> None :
55- warnings .warn ("warning raised while handling the request" , stacklevel = 1 )
58+ warnings .warn ("first warning raised while handling the request" , stacklevel = 1 )
59+ warnings .warn ("second warning raised while handling the request" , stacklevel = 1 )
5660
5761 server ._handle_request = _handle_request_that_warns # type: ignore[assignment]
5862
@@ -75,4 +79,7 @@ async def _handle_request_that_warns(*args: object, **kwargs: object) -> None:
7579 server_logger .removeHandler (handler )
7680 server_logger .setLevel (previous_level )
7781
78- assert handler .emit_count == 1
82+ # Two warnings were recorded during handling, so the handler is invoked
83+ # exactly twice. A regressed build re-appends the handler's own warning and
84+ # invokes it a third time.
85+ assert handler .emit_count == 2
0 commit comments