diff --git a/changelog/1476.bugfix.rst b/changelog/1476.bugfix.rst new file mode 100644 index 0000000000..9652655425 --- /dev/null +++ b/changelog/1476.bugfix.rst @@ -0,0 +1 @@ +Fix ``utils.warn_deprecated`` to respect :mod:`warnings` filters defined by user. diff --git a/disnake/utils.py b/disnake/utils.py index 2cb4ba5674..77907212df 100644 --- a/disnake/utils.py +++ b/disnake/utils.py @@ -301,8 +301,17 @@ def warn_deprecated( stacklevel = 1 # reset stacklevel, assume we just want the first frame outside library code old_filters = warnings.filters[:] + if len(old_filters) > 0: + for action, _, category, module, _ in old_filters: + if ( + (category is DeprecationWarning) + and ("disnake" in str(module)) + and (action == "ignore") + ): + return # If a disnake ignore rule is found, we skip warning. + # We allow force bypassing of filters if the default/global DeprecationWarning ignore is set. try: - warnings.simplefilter("always", DeprecationWarning) + warnings.simplefilter(action="always", category=DeprecationWarning) warnings.warn(*args, stacklevel=stacklevel + 1, category=DeprecationWarning, **kwargs) finally: assert isinstance(warnings.filters, list) diff --git a/tests/test_utils.py b/tests/test_utils.py index 84fe54400c..b57999bdfc 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -127,6 +127,48 @@ def func(n: int) -> None: assert result[0].filename == mock.__file__ +@pytest.mark.parametrize( + "msg", + ["This is a deprecated function"], +) +def test_deprecated_warn(msg: str) -> None: + # Just test if works + # first clear existing filters - these will be restored at the end as this is probably messing with the global warning filters + filters = warnings.filters[:] + + warnings.resetwarnings() + with warnings.catch_warnings(record=True) as result: + warnings.filterwarnings("always", category=DeprecationWarning, module=r"disnake\..*") + utils.warn_deprecated(msg) + + assert len(result) == 1, "Expected one warning to be raised." + assert result[0].category is DeprecationWarning + assert result[0].message.args[0] == msg # pyright: ignore[reportAttributeAccessIssue] + + # Reset With empty filters. + # Expected behaviour is that a warning is forced because it's empty. + warnings.resetwarnings() + with warnings.catch_warnings(record=True) as result: + utils.warn_deprecated(msg) + + assert len(result) == 1, "Expected one warning to be raised." + assert result[0].category is DeprecationWarning + assert result[0].message.args[0] == msg # pyright: ignore[reportAttributeAccessIssue] + + # Reset With empty filters and add disnake ignore rule. + # Expected behaviour is that there is no warning because it's ignored. + warnings.resetwarnings() + with warnings.catch_warnings(record=True) as result: + warnings.filterwarnings("ignore", category=DeprecationWarning, module=r"disnake\..*") + utils.warn_deprecated(msg) + + assert len(result) == 0, ( + "Expected no warnings to be raised when declaring disnake to be ignored." + ) + + warnings.filters[:] = filters # pyright: ignore[reportIndexIssue] + + @pytest.mark.parametrize( ("params", "expected"), [