muutils.collect_warnings
1from __future__ import annotations 2 3import sys 4import warnings 5from collections import Counter 6from contextlib import AbstractContextManager 7from types import TracebackType 8from typing import Any, Literal 9 10 11class CollateWarnings(AbstractContextManager["CollateWarnings"]): 12 """Capture every warning issued inside a `with` block and print a collated 13 summary when the block exits. 14 15 Internally this wraps `warnings.catch_warnings(record=True)` so that all 16 warnings raised in the block are recorded. When the context exits, identical 17 warnings are grouped and (optionally) printed with a user-defined format. 18 19 # Parameters: 20 - `print_on_exit : bool` 21 Whether to print the summary when the context exits 22 (defaults to `True`) 23 - `fmt : str` 24 Format string used for printing each line of the summary. 25 Available fields are: 26 27 * `{count}` : number of occurrences 28 * `{filename}` : file where the warning originated 29 * `{lineno}` : line number 30 * `{category}` : warning class name 31 * `{message}` : warning message text 32 33 (defaults to `"({count}x) {filename}:{lineno} {category}: {message}"`) 34 35 # Returns: 36 - `CollateWarnings` 37 The context-manager instance. After exit, the attribute 38 `counts` holds a mapping 39 40 ```python 41 {(filename, lineno, category, message): count} 42 ``` 43 44 # Usage: 45 ```python 46 >>> import warnings 47 >>> with CollateWarnings() as cw: 48 ... warnings.warn("deprecated", DeprecationWarning) 49 ... warnings.warn("deprecated", DeprecationWarning) 50 ... warnings.warn("other", UserWarning) 51 (2x) /tmp/example.py:42 DeprecationWarning: deprecated 52 (1x) /tmp/example.py:43 UserWarning: other 53 >>> cw.counts 54 {('/tmp/example.py', 42, 'DeprecationWarning', 'deprecated'): 2, 55 ('/tmp/example.py', 43, 'UserWarning', 'other'): 1} 56 ``` 57 """ 58 59 _active: bool 60 _catcher: Any 61 _records: list[warnings.WarningMessage] 62 counts: Counter[ 63 tuple[ 64 str, # filename 65 int, # lineno 66 str, # category name 67 str, # message 68 ] 69 ] 70 print_on_exit: bool 71 fmt: str 72 73 def __init__( 74 self, 75 print_on_exit: bool = True, 76 fmt: str = "({count}x) {filename}:{lineno} {category}: {message}", 77 ) -> None: 78 self.print_on_exit = print_on_exit 79 self.fmt = fmt 80 self._active = False 81 self._records = [] 82 self.counts = Counter() 83 84 def __enter__(self) -> CollateWarnings: 85 if self._active: 86 raise RuntimeError("CollateWarnings cannot be re-entered") 87 88 self._active = True 89 self._catcher = warnings.catch_warnings(record=True) 90 self._records = self._catcher.__enter__() 91 warnings.simplefilter("always") # capture every warning 92 return self 93 94 def __exit__( 95 self, 96 exc_type: type[BaseException] | None, 97 exc_val: BaseException | None, 98 exc_tb: TracebackType | None, 99 ) -> Literal[False]: 100 if not self._active: 101 raise RuntimeError("CollateWarnings exited twice") 102 103 self._active = False 104 # stop capturing 105 self._catcher.__exit__(exc_type, exc_val, exc_tb) 106 107 # collate 108 self.counts = Counter( 109 ( 110 rec.filename, 111 rec.lineno, 112 rec.category.__name__, 113 str(rec.message), 114 ) 115 for rec in self._records 116 ) 117 118 if self.print_on_exit: 119 for (filename, lineno, category, message), count in self.counts.items(): 120 print( 121 self.fmt.format( 122 count=count, 123 filename=filename, 124 lineno=lineno, 125 category=category, 126 message=message, 127 ), 128 file=sys.stderr, 129 ) 130 131 # propagate any exception from the with-block 132 return False
class
CollateWarnings(contextlib.AbstractContextManager['CollateWarnings']):
12class CollateWarnings(AbstractContextManager["CollateWarnings"]): 13 """Capture every warning issued inside a `with` block and print a collated 14 summary when the block exits. 15 16 Internally this wraps `warnings.catch_warnings(record=True)` so that all 17 warnings raised in the block are recorded. When the context exits, identical 18 warnings are grouped and (optionally) printed with a user-defined format. 19 20 # Parameters: 21 - `print_on_exit : bool` 22 Whether to print the summary when the context exits 23 (defaults to `True`) 24 - `fmt : str` 25 Format string used for printing each line of the summary. 26 Available fields are: 27 28 * `{count}` : number of occurrences 29 * `{filename}` : file where the warning originated 30 * `{lineno}` : line number 31 * `{category}` : warning class name 32 * `{message}` : warning message text 33 34 (defaults to `"({count}x) {filename}:{lineno} {category}: {message}"`) 35 36 # Returns: 37 - `CollateWarnings` 38 The context-manager instance. After exit, the attribute 39 `counts` holds a mapping 40 41 ```python 42 {(filename, lineno, category, message): count} 43 ``` 44 45 # Usage: 46 ```python 47 >>> import warnings 48 >>> with CollateWarnings() as cw: 49 ... warnings.warn("deprecated", DeprecationWarning) 50 ... warnings.warn("deprecated", DeprecationWarning) 51 ... warnings.warn("other", UserWarning) 52 (2x) /tmp/example.py:42 DeprecationWarning: deprecated 53 (1x) /tmp/example.py:43 UserWarning: other 54 >>> cw.counts 55 {('/tmp/example.py', 42, 'DeprecationWarning', 'deprecated'): 2, 56 ('/tmp/example.py', 43, 'UserWarning', 'other'): 1} 57 ``` 58 """ 59 60 _active: bool 61 _catcher: Any 62 _records: list[warnings.WarningMessage] 63 counts: Counter[ 64 tuple[ 65 str, # filename 66 int, # lineno 67 str, # category name 68 str, # message 69 ] 70 ] 71 print_on_exit: bool 72 fmt: str 73 74 def __init__( 75 self, 76 print_on_exit: bool = True, 77 fmt: str = "({count}x) {filename}:{lineno} {category}: {message}", 78 ) -> None: 79 self.print_on_exit = print_on_exit 80 self.fmt = fmt 81 self._active = False 82 self._records = [] 83 self.counts = Counter() 84 85 def __enter__(self) -> CollateWarnings: 86 if self._active: 87 raise RuntimeError("CollateWarnings cannot be re-entered") 88 89 self._active = True 90 self._catcher = warnings.catch_warnings(record=True) 91 self._records = self._catcher.__enter__() 92 warnings.simplefilter("always") # capture every warning 93 return self 94 95 def __exit__( 96 self, 97 exc_type: type[BaseException] | None, 98 exc_val: BaseException | None, 99 exc_tb: TracebackType | None, 100 ) -> Literal[False]: 101 if not self._active: 102 raise RuntimeError("CollateWarnings exited twice") 103 104 self._active = False 105 # stop capturing 106 self._catcher.__exit__(exc_type, exc_val, exc_tb) 107 108 # collate 109 self.counts = Counter( 110 ( 111 rec.filename, 112 rec.lineno, 113 rec.category.__name__, 114 str(rec.message), 115 ) 116 for rec in self._records 117 ) 118 119 if self.print_on_exit: 120 for (filename, lineno, category, message), count in self.counts.items(): 121 print( 122 self.fmt.format( 123 count=count, 124 filename=filename, 125 lineno=lineno, 126 category=category, 127 message=message, 128 ), 129 file=sys.stderr, 130 ) 131 132 # propagate any exception from the with-block 133 return False
Capture every warning issued inside a with block and print a collated
summary when the block exits.
Internally this wraps warnings.catch_warnings(record=True) so that all
warnings raised in the block are recorded. When the context exits, identical
warnings are grouped and (optionally) printed with a user-defined format.
Parameters:
print_on_exit : boolWhether to print the summary when the context exits (defaults toTrue)fmt : strFormat string used for printing each line of the summary. Available fields are:{count}: number of occurrences{filename}: file where the warning originated{lineno}: line number{category}: warning class name{message}: warning message text
(defaults to
"({count}x) {filename}:{lineno} {category}: {message}")
Returns:
CollateWarningsThe context-manager instance. After exit, the attributecountsholds a mapping{(filename, lineno, category, message): count}
Usage:
>>> import warnings
>>> with CollateWarnings() as cw:
... warnings.warn("deprecated", DeprecationWarning)
... warnings.warn("deprecated", DeprecationWarning)
... warnings.warn("other", UserWarning)
(2x) /tmp/example.py:42 DeprecationWarning: deprecated
(1x) /tmp/example.py:43 UserWarning: other
>>> cw.counts
{('/tmp/example.py', 42, 'DeprecationWarning', 'deprecated'): 2,
('/tmp/example.py', 43, 'UserWarning', 'other'): 1}