Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/1476.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
warn_deprecated now respected warning.filter ignores if at least "disnake" is passed to module argument.
27 changes: 21 additions & 6 deletions disnake/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,12 +301,27 @@ def warn_deprecated(
stacklevel = 1 # reset stacklevel, assume we just want the first frame outside library code

old_filters = warnings.filters[:]
try:
warnings.simplefilter("always", DeprecationWarning)
warnings.warn(*args, stacklevel=stacklevel + 1, category=DeprecationWarning, **kwargs)
finally:
assert isinstance(warnings.filters, list)
warnings.filters[:] = old_filters
warnings.filterwarnings(action="default", category=DeprecationWarning, module="cumbum")
send_warning = True
if len(old_filters) > 0:
for action, _, category, module, _ in old_filters:
if (
(category is DeprecationWarning)
and ("disnake" in str(module))
and (action == "ignore")
):
send_warning = False
break # break out after first disnake rule, it's good enough.

if send_warning:
# this still allows force bypassing of filters if the default DeprecationWarning ignore is set.
try:
warnings.simplefilter(action="always", category=DeprecationWarning)
warnings.warn(*args, stacklevel=stacklevel + 1, category=DeprecationWarning, **kwargs)
finally:
# NOTE: Is this assertion even necessary? warnings.filters is always at minimum an empty list?
assert isinstance(warnings.filters, list)
warnings.filters[:] = old_filters
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if send_warning:
# this still allows force bypassing of filters if the default DeprecationWarning ignore is set.
try:
warnings.simplefilter(action="always", category=DeprecationWarning)
warnings.warn(*args, stacklevel=stacklevel + 1, category=DeprecationWarning, **kwargs)
finally:
# NOTE: Is this assertion even necessary? warnings.filters is always at minimum an empty list?
assert isinstance(warnings.filters, list)
warnings.filters[:] = old_filters
# allow force bypassing of filters if the default DeprecationWarning ignore is set.
if not send_warning:
return
try:
warnings.simplefilter(action="always", category=DeprecationWarning)
warnings.warn(*args, stacklevel=stacklevel + 1, category=DeprecationWarning, **kwargs)
finally:
assert isinstance(warnings.filters, list)
warnings.filters[:] = old_filters

Copy link
Author

@hularuns hularuns Nov 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree on the guard clause instead and committed your suggestion. Just thought, after committing we could instead return within the for loop of old_filters instead of assigning send_warning a value and breaking from the loop. Personally, I don't like that as it's not as explicit

Copy link
Contributor

@Enegg Enegg Nov 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bool variable can be avoided by using a for ... else clause, while still keeping it flat.

for ... in ...:
    if condition:
        break
else:
    return

# here goes stuff that should run when condition is true

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of the early break, i've settled for doing an early return here instead



def oauth_url(
Expand Down
42 changes: 42 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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\..*"
) # this also works with just a plain str
utils.warn_deprecated(msg)

assert len(result) == 1
assert result[0].message.args[0] == msg # pyright: ignore[reportAttributeAccessIssue]
assert result[0].category is DeprecationWarning

# Reset With empty filters - expected behaviour is that a warning is forced because it's empty
warnings.resetwarnings()
assert len(warnings.filters) == 0 # should be empty
with warnings.catch_warnings(record=True) as result:
utils.warn_deprecated(msg)

assert len(result) == 1
assert result[0].message.args[0] == msg # pyright: ignore[reportAttributeAccessIssue]
assert result[0].category is DeprecationWarning

# Reset With empty filters - expected behaviour is that there is no warning because it's ignored
warnings.resetwarnings()
assert len(warnings.filters) == 0 # should be empty
with warnings.catch_warnings(record=True) as result:
warnings.filterwarnings("ignore", category=DeprecationWarning, module=r"disnake\..*")
utils.warn_deprecated(msg)

assert len(result) == 0

warnings.filters[:] = filters # pyright: ignore[reportIndexIssue]


@pytest.mark.parametrize(
("params", "expected"),
[
Expand Down
Loading