@@ -289,6 +289,12 @@ def _log_item(item):
289289 return item
290290
291291
292+ def _warn_on_item (item ):
293+ logging .getLogger (WORKER ).info (f"handled { item } " )
294+ logging .getLogger (WORKER ).warning (f"trouble with { item } " )
295+ return item
296+
297+
292298def test_worker_records_are_replayed_in_input_order (collected ):
293299 """The tasks finish in whatever order they finish, so the replay is what
294300 puts the messages back in the order of the iterable."""
@@ -303,12 +309,64 @@ def test_worker_records_are_replayed_in_input_order(collected):
303309
304310
305311def test_worker_records_are_dropped_without_flush_at_end (collected ):
306- """flush_at_end=False means quiet, which is what debug=False asks for."""
312+ """flush_at_end=False means quiet for progress, which is what debug=False
313+ asks for."""
307314 parallelize (_log_item , [1 , 2 , 3 ], flush_at_end = False , progress_bar = False )
308315
309316 assert collected .messages (WORKER ) == []
310317
311318
319+ def test_worker_warnings_survive_a_quiet_batch (collected ):
320+ """A warning reports a problem rather than progress, so a batch asked to
321+ stay quiet still passes it on. These call sites used warnings.warn before,
322+ which parallelize never suppressed, so dropping them would lose output a
323+ caller has today."""
324+ parallelize (_warn_on_item , [1 , 2 ], flush_at_end = False , progress_bar = False )
325+
326+ assert collected .messages (WORKER ) == ["trouble with 1" , "trouble with 2" ]
327+
328+
329+ def test_worker_errors_survive_a_quiet_batch (collected ):
330+ """Errors are replayed on the same grounds as warnings. OSW.delete_entity
331+ logs one from inside a worker it parallelizes, at src/osw/core.py."""
332+
333+ def _fail_quietly (item ):
334+ logging .getLogger (WORKER ).error (f"could not handle { item } " )
335+
336+ parallelize (_fail_quietly , [1 ], flush_at_end = False , progress_bar = False )
337+
338+ assert collected .messages (WORKER ) == ["could not handle 1" ]
339+
340+
341+ def test_worker_warnings_reach_pytests_caplog (caplog , osw_logger ):
342+ """caplog puts its handler on the root logger, so a replayed record has to
343+ travel the same path as any other to be seen. The integration test
344+ tests/integration/performance_test.py asserts on a warning from a parallel
345+ get_page batch this way, and it needs wiki credentials to run."""
346+ caplog .set_level (logging .WARNING , logger = "osw" )
347+
348+ parallelize (_warn_on_item , [1 ], flush_at_end = False , progress_bar = False )
349+
350+ assert any ("trouble with 1" in record .message for record in caplog .records )
351+
352+
353+ def test_worker_warnings_are_replayed_in_input_order (collected ):
354+ """The tasks finish in whatever order they finish, so the warnings are put
355+ back in the order of the iterable like everything else."""
356+ parallelize (_warn_on_item , [1 , 2 , 3 , 4 ], flush_at_end = True , progress_bar = False )
357+
358+ assert collected .messages (WORKER ) == [
359+ "handled 1" ,
360+ "trouble with 1" ,
361+ "handled 2" ,
362+ "trouble with 2" ,
363+ "handled 3" ,
364+ "trouble with 3" ,
365+ "handled 4" ,
366+ "trouble with 4" ,
367+ ]
368+
369+
312370def test_worker_records_reach_the_application_handler (osw_logger ):
313371 """The capture replays into whatever the handler chain holds, so a batch's
314372 records land in the application's handlers like any other."""
0 commit comments