logging.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. """
  2. Helper functionality for interoperability with stdlib `logging`.
  3. """
  4. import logging
  5. import sys
  6. from contextlib import contextmanager
  7. try:
  8. from typing import Iterator, List, Optional, Type # noqa: F401
  9. except ImportError:
  10. pass
  11. from ..std import tqdm as std_tqdm
  12. class _TqdmLoggingHandler(logging.StreamHandler):
  13. def __init__(
  14. self,
  15. tqdm_class=std_tqdm # type: Type[std_tqdm]
  16. ):
  17. super(_TqdmLoggingHandler, self).__init__()
  18. self.tqdm_class = tqdm_class
  19. def emit(self, record):
  20. try:
  21. msg = self.format(record)
  22. self.tqdm_class.write(msg, file=self.stream)
  23. self.flush()
  24. except (KeyboardInterrupt, SystemExit):
  25. raise
  26. except: # noqa pylint: disable=bare-except
  27. self.handleError(record)
  28. def _is_console_logging_handler(handler):
  29. return (isinstance(handler, logging.StreamHandler)
  30. and handler.stream in {sys.stdout, sys.stderr})
  31. def _get_first_found_console_logging_handler(handlers):
  32. for handler in handlers:
  33. if _is_console_logging_handler(handler):
  34. return handler
  35. @contextmanager
  36. def logging_redirect_tqdm(
  37. loggers=None, # type: Optional[List[logging.Logger]],
  38. tqdm_class=std_tqdm # type: Type[std_tqdm]
  39. ):
  40. # type: (...) -> Iterator[None]
  41. """
  42. Context manager redirecting console logging to `tqdm.write()`, leaving
  43. other logging handlers (e.g. log files) unaffected.
  44. Parameters
  45. ----------
  46. loggers : list, optional
  47. Which handlers to redirect (default: [logging.root]).
  48. tqdm_class : optional
  49. Example
  50. -------
  51. ```python
  52. import logging
  53. from tqdm import trange
  54. from tqdm.contrib.logging import logging_redirect_tqdm
  55. LOG = logging.getLogger(__name__)
  56. if __name__ == '__main__':
  57. logging.basicConfig(level=logging.INFO)
  58. with logging_redirect_tqdm():
  59. for i in trange(9):
  60. if i == 4:
  61. LOG.info("console logging redirected to `tqdm.write()`")
  62. # logging restored
  63. ```
  64. """
  65. if loggers is None:
  66. loggers = [logging.root]
  67. original_handlers_list = [logger.handlers for logger in loggers]
  68. try:
  69. for logger in loggers:
  70. tqdm_handler = _TqdmLoggingHandler(tqdm_class)
  71. orig_handler = _get_first_found_console_logging_handler(logger.handlers)
  72. if orig_handler is not None:
  73. tqdm_handler.setFormatter(orig_handler.formatter)
  74. tqdm_handler.stream = orig_handler.stream
  75. logger.handlers = [
  76. handler for handler in logger.handlers
  77. if not _is_console_logging_handler(handler)] + [tqdm_handler]
  78. yield
  79. finally:
  80. for logger, original_handlers in zip(loggers, original_handlers_list):
  81. logger.handlers = original_handlers
  82. @contextmanager
  83. def tqdm_logging_redirect(
  84. *args,
  85. # loggers=None, # type: Optional[List[logging.Logger]]
  86. # tqdm=None, # type: Optional[Type[tqdm.tqdm]]
  87. **kwargs
  88. ):
  89. # type: (...) -> Iterator[None]
  90. """
  91. Convenience shortcut for:
  92. ```python
  93. with tqdm_class(*args, **tqdm_kwargs) as pbar:
  94. with logging_redirect_tqdm(loggers=loggers, tqdm_class=tqdm_class):
  95. yield pbar
  96. ```
  97. Parameters
  98. ----------
  99. tqdm_class : optional, (default: tqdm.std.tqdm).
  100. loggers : optional, list.
  101. **tqdm_kwargs : passed to `tqdm_class`.
  102. """
  103. tqdm_kwargs = kwargs.copy()
  104. loggers = tqdm_kwargs.pop('loggers', None)
  105. tqdm_class = tqdm_kwargs.pop('tqdm_class', std_tqdm)
  106. with tqdm_class(*args, **tqdm_kwargs) as pbar:
  107. with logging_redirect_tqdm(loggers=loggers, tqdm_class=tqdm_class):
  108. yield pbar