@@ -374,7 +374,21 @@ def emit(self, record: logging.LogRecord) -> None:
374374
375375
376376class LoggingManager :
377- """Capture task logs for reports and optional log files."""
377+ """Capture task logs for reports and optional log files.
378+
379+ This intentionally follows pytest's handler-on-root design instead of trying to
380+ intercept logging internals. The tradeoff is that task-local logging
381+ reconfiguration is not fully isolated from pytask:
382+
383+ - ``logging.basicConfig()`` inside a task can become a no-op because pytask has
384+ already attached a handler to the root logger.
385+ - ``logging.basicConfig(force=True)`` or direct mutation of ``root.handlers`` can
386+ remove or close pytask-managed handlers for the remainder of the task/session.
387+
388+ We accept these limitations for parity with pytest and to keep the integration
389+ simple. A more isolated design would need to avoid attaching handlers to the root
390+ logger in the first place.
391+ """
378392
379393 def __init__ ( # noqa: PLR0913
380394 self ,
@@ -450,6 +464,9 @@ def _catching_logs(self) -> Generator[None, None, None]:
450464 ]
451465
452466 try :
467+ # Attaching handlers to the root logger is the key design choice here. It
468+ # keeps pytask aligned with pytest, but it also means task code that
469+ # reconfigures the root logger can affect pytask's own logging handlers.
453470 for handler in handlers :
454471 handler .setLevel (
455472 self .log_level
@@ -527,6 +544,9 @@ def _create_log_file_handler(
527544 return None
528545
529546 log_file .parent .mkdir (parents = True , exist_ok = True )
547+ # This handler is session-scoped and reattached around each task. If user code
548+ # force-reconfigures the root logger, Python may close this handler as part of
549+ # removing existing root handlers.
530550 log_file_handler = logging .FileHandler (
531551 log_file , mode = log_file_mode , encoding = "utf-8"
532552 )
0 commit comments