Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pytest.warns multiple argument handling #11917

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/11906.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix regression with :func:`pytest.warns` using custom warning subclasses which have more than one parameter in their `__init__`.
24 changes: 11 additions & 13 deletions src/_pytest/recwarn.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,11 @@ def recwarn() -> Generator["WarningsRecorder", None, None]:
@overload
def deprecated_call(
*, match: Optional[Union[str, Pattern[str]]] = ...
) -> "WarningsRecorder":
...
) -> "WarningsRecorder": ...


@overload
def deprecated_call(func: Callable[..., T], *args: Any, **kwargs: Any) -> T:
...
def deprecated_call(func: Callable[..., T], *args: Any, **kwargs: Any) -> T: ...


def deprecated_call(
Expand Down Expand Up @@ -90,8 +88,7 @@ def warns(
expected_warning: Union[Type[Warning], Tuple[Type[Warning], ...]] = ...,
*,
match: Optional[Union[str, Pattern[str]]] = ...,
) -> "WarningsChecker":
...
) -> "WarningsChecker": ...


@overload
Expand All @@ -100,8 +97,7 @@ def warns(
func: Callable[..., T],
*args: Any,
**kwargs: Any,
) -> T:
...
) -> T: ...


def warns(
Expand Down Expand Up @@ -322,10 +318,10 @@ def found_str():
for w in self:
if not self.matches(w):
warnings.warn_explicit(
str(w.message),
w.message.__class__, # type: ignore[arg-type]
w.filename,
w.lineno,
message=w.message,
category=w.category,
filename=w.filename,
lineno=w.lineno,
module=w.__module__,
bluetech marked this conversation as resolved.
Show resolved Hide resolved
source=w.source,
)
Expand All @@ -336,7 +332,9 @@ def found_str():

@staticmethod
def _validate_message(wrn: Any) -> None:
if not isinstance(msg := wrn.message.args[0], str):
Copy link
Member

Choose a reason for hiding this comment

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

Can you explain these changes?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After making the other changes, the code failed some tests from #11804 that solved #10865, where the incorrect arguments then defaulted to becoming UserWarnings. However, the implementation also caught the custom CustomWarning class (where wrn.message.args[0] = int from CustomWarning's __init__, which this looked to fix.
This looks related to #11954

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hm, this looks like it could be its own PR potentially. Another implementation: #11959

if type(wrn.message) is UserWarning and not isinstance(
msg := wrn.message.args[0], str
):
raise TypeError(
f"Warning message must be str, got {msg!r} (type {type(msg).__name__})"
)
14 changes: 14 additions & 0 deletions testing/test_recwarn.py
Original file line number Diff line number Diff line change
Expand Up @@ -504,3 +504,17 @@ def test_raise_type_error_on_non_string_warning_cpython() -> None:
with warnings.catch_warnings():
warnings.filterwarnings("ignore", "test")
warnings.warn(1) # type: ignore


def test_multiple_arg_custom_warning() -> None:
"""Test for issue #11906."""

class CustomWarning(UserWarning):
def __init__(self, a, b):
pass

with pytest.warns(CustomWarning):
with pytest.raises(pytest.fail.Exception, match="DID NOT WARN"):
with pytest.warns(CustomWarning, match="not gonna match"):
a, b = 1, 2
warnings.warn(CustomWarning(a, b))