1818import warnings
1919import importlib
2020import inspect
21+ import functools
2122from types import ModuleType
2223from functools import wraps
2324from typing import (
3435 MutableMapping ,
3536 Collection ,
3637 cast ,
37- overload
38+ overload ,
3839)
3940
41+ from .typing_utils import Literal
4042from .empty import EMPTY_CONTAINER
4143
4244__all__ = [
5860 'positional_only' ,
5961 'UserMapping' ,
6062 'MutableUserMapping' ,
63+ 'warning_filter' ,
6164]
6265
6366_T = TypeVar ('_T' )
@@ -737,6 +740,75 @@ def positional_only(func: _FT) -> _FT:
737740 return func
738741
739742
743+ def warning_filter (
744+ action : Literal ["default" , "error" , "ignore" , "always" , "module" , "once" ],
745+ message : str = "" ,
746+ category : type [Warning ] = Warning ,
747+ module : str = "" ,
748+ lineno : int = 0 ,
749+ append : bool = False ,
750+ ) -> Callable [[_FT ], _FT ]:
751+ """A decorator for wrapping function calls with :func:`warnings.filterwarnings`.
752+
753+ Examples
754+ --------
755+ .. code-block:: python
756+
757+ >>> from nanoutils import warning_filter
758+ >>> import warnings
759+
760+ >>> @warning_filter("error", category=UserWarning)
761+ ... def func():
762+ ... warnings.warn("test", UserWarning)
763+
764+ >>> func()
765+ Traceback (most recent call last):
766+ ...
767+ UserWarning: test
768+
769+ Parameters
770+ ----------
771+ action : :class:`str`
772+ One of the following strings:
773+
774+ * ``"default"``: Print the first occurrence of matching warnings for each location (module + line number) where the warning is issued
775+ * ``"error"``: Turn matching warnings into exceptions
776+ * ``"ignore"``: Never print matching warnings
777+ * ``"always"``: Always print matching warnings
778+ * ``"module"``: Print the first occurrence of matching warnings for each module where the warning is issued (regardless of line number)
779+ * ``"once"``: Print only the first occurrence of matching warnings, regardless of location
780+
781+ message : :class:`str`, optional
782+ A string containing a regular expression that the start of the warning message must match.
783+ The expression is compiled to always be case-insensitive.
784+ category : :class:`type[Warning] <type>`
785+ The to-be affected :class:`Warning` (sub-)class.
786+ module : :class:`str`, optional
787+ A string containing a regular expression that the module name must match.
788+ The expression is compiled to be case-sensitive.
789+ lineno : :class:`int`
790+ An integer that the line number where the warning occurred must match,
791+ or 0 to match all line numbers.
792+ append : :class:`bool`
793+ Whether the warning entry is inserted at the end.
794+
795+ See Also
796+ --------
797+ :func:`warnings.filterwarnings` :
798+ Insert a simple entry into the list of warnings filters (at the front).
799+
800+ """
801+ def decorator (func : _FT ) -> _FT :
802+ @functools .wraps (func )
803+ def wrapper (* args , ** kwargs ):
804+ with warnings .catch_warnings ():
805+ warnings .filterwarnings (action , message , category , module , lineno , append )
806+ ret = func (* args , ** kwargs )
807+ return ret
808+ return cast (_FT , wrapper )
809+ return decorator
810+
811+
740812# Move to the end to reduce the risk of circular imports
741813from ._partial import PartialPrepend
742814from ._set_attr import SetAttr
@@ -747,5 +819,5 @@ def positional_only(func: _FT) -> _FT:
747819
748820__doc__ = construct_api_doc (
749821 globals (),
750- decorators = {'set_docstring' , 'raise_if' , 'ignore_if' , 'positional_only' },
822+ decorators = {'set_docstring' , 'raise_if' , 'ignore_if' , 'positional_only' , 'warning_filter' },
751823)
0 commit comments