From 5feabaab416c65457eb0377464807dbe067a4f18 Mon Sep 17 00:00:00 2001 From: Reagan Lee <96998476+reaganjlee@users.noreply.github.com> Date: Sat, 3 Feb 2024 16:06:02 -0800 Subject: [PATCH 1/6] add test --- testing/test_recwarn.py | 33 +++++++++------------------------ 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/testing/test_recwarn.py b/testing/test_recwarn.py index e269bd7ddc9..d8a6301b3f1 100644 --- a/testing/test_recwarn.py +++ b/testing/test_recwarn.py @@ -479,28 +479,13 @@ def test_catch_warning_within_raise(self) -> None: warnings.warn("some warning", category=FutureWarning) raise ValueError("some exception") + def test_multiple_arg_custom_warning(self) -> None: + class CustomWarning(UserWarning): + def __init__(self, a, b): + pass -def test_raise_type_error_on_non_string_warning() -> None: - """Check pytest.warns validates warning messages are strings (#10865).""" - with pytest.raises(TypeError, match="Warning message must be str"): - with pytest.warns(UserWarning): - warnings.warn(1) # type: ignore - - -def test_no_raise_type_error_on_string_warning() -> None: - """Check pytest.warns validates warning messages are strings (#10865).""" - with pytest.warns(UserWarning): - warnings.warn("Warning") - - -@pytest.mark.skipif( - hasattr(sys, "pypy_version_info"), - reason="Not for pypy", -) -def test_raise_type_error_on_non_string_warning_cpython() -> None: - # Check that we get the same behavior with the stdlib, at least if filtering - # (see https://github.com/python/cpython/issues/103577 for details) - with pytest.raises(TypeError): - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", "test") - warnings.warn(1) # type: ignore + 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)) From 497ffc51c1c88aea769035abe74890bd4b5129f6 Mon Sep 17 00:00:00 2001 From: Reagan Lee <96998476+reaganjlee@users.noreply.github.com> Date: Sat, 3 Feb 2024 15:39:49 -0800 Subject: [PATCH 2/6] bug fix implement @bluetech changes implement code review changes --- changelog/11906.bugfix.rst | 1 + src/_pytest/recwarn.py | 8 ++++---- testing/test_recwarn.py | 2 ++ 3 files changed, 7 insertions(+), 4 deletions(-) create mode 100644 changelog/11906.bugfix.rst diff --git a/changelog/11906.bugfix.rst b/changelog/11906.bugfix.rst new file mode 100644 index 00000000000..68bede540a8 --- /dev/null +++ b/changelog/11906.bugfix.rst @@ -0,0 +1 @@ +Fix regression with :func:`pytest.warns` using custom warning subclasses which have more than one parameter in their `__init__`. diff --git a/src/_pytest/recwarn.py b/src/_pytest/recwarn.py index 62df274bd37..e89fecd1290 100644 --- a/src/_pytest/recwarn.py +++ b/src/_pytest/recwarn.py @@ -322,10 +322,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__, source=w.source, ) diff --git a/testing/test_recwarn.py b/testing/test_recwarn.py index d8a6301b3f1..8f39818dbe9 100644 --- a/testing/test_recwarn.py +++ b/testing/test_recwarn.py @@ -480,6 +480,8 @@ def test_catch_warning_within_raise(self) -> None: raise ValueError("some exception") def test_multiple_arg_custom_warning(self) -> None: + """Test for issue #11906.""" + class CustomWarning(UserWarning): def __init__(self, a, b): pass From 02c3fffc49feef1911b0635b67833da0baea6355 Mon Sep 17 00:00:00 2001 From: Reagan Lee <96998476+reaganjlee@users.noreply.github.com> Date: Thu, 8 Feb 2024 15:13:33 -0800 Subject: [PATCH 3/6] restore deleted tests --- testing/test_recwarn.py | 47 ++++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/testing/test_recwarn.py b/testing/test_recwarn.py index 8f39818dbe9..c7eac6c40b6 100644 --- a/testing/test_recwarn.py +++ b/testing/test_recwarn.py @@ -479,15 +479,42 @@ def test_catch_warning_within_raise(self) -> None: warnings.warn("some warning", category=FutureWarning) raise ValueError("some exception") - def test_multiple_arg_custom_warning(self) -> None: - """Test for issue #11906.""" - class CustomWarning(UserWarning): - def __init__(self, a, b): - pass +def test_raise_type_error_on_non_string_warning() -> None: + """Check pytest.warns validates warning messages are strings (#10865).""" + with pytest.raises(TypeError, match="Warning message must be str"): + with pytest.warns(UserWarning): + warnings.warn(1) # type: ignore - 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)) + +def test_no_raise_type_error_on_string_warning() -> None: + """Check pytest.warns validates warning messages are strings (#10865).""" + with pytest.warns(UserWarning): + warnings.warn("Warning") + + +@pytest.mark.skipif( + hasattr(sys, "pypy_version_info"), + reason="Not for pypy", +) +def test_raise_type_error_on_non_string_warning_cpython() -> None: + # Check that we get the same behavior with the stdlib, at least if filtering + # (see https://github.com/python/cpython/issues/103577 for details) + with pytest.raises(TypeError): + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", "test") + warnings.warn(1) # type: ignore + + +def test_multiple_arg_custom_warning(self) -> 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)) From 1b3ac3659edb29665740bad8afa7880fd5072b52 Mon Sep 17 00:00:00 2001 From: Reagan Lee <96998476+reaganjlee@users.noreply.github.com> Date: Thu, 8 Feb 2024 15:36:17 -0800 Subject: [PATCH 4/6] tiny bug --- testing/test_recwarn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/test_recwarn.py b/testing/test_recwarn.py index c7eac6c40b6..2a1528eb9ce 100644 --- a/testing/test_recwarn.py +++ b/testing/test_recwarn.py @@ -506,7 +506,7 @@ def test_raise_type_error_on_non_string_warning_cpython() -> None: warnings.warn(1) # type: ignore -def test_multiple_arg_custom_warning(self) -> None: +def test_multiple_arg_custom_warning() -> None: """Test for issue #11906.""" class CustomWarning(UserWarning): From 6e61340f9169cc78d94d14a09864e283e821a770 Mon Sep 17 00:00:00 2001 From: Reagan Lee <96998476+reaganjlee@users.noreply.github.com> Date: Thu, 8 Feb 2024 16:03:09 -0800 Subject: [PATCH 5/6] update to solve other test --- src/_pytest/recwarn.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/_pytest/recwarn.py b/src/_pytest/recwarn.py index e89fecd1290..ec91bc4841b 100644 --- a/src/_pytest/recwarn.py +++ b/src/_pytest/recwarn.py @@ -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( @@ -90,8 +88,7 @@ def warns( expected_warning: Union[Type[Warning], Tuple[Type[Warning], ...]] = ..., *, match: Optional[Union[str, Pattern[str]]] = ..., -) -> "WarningsChecker": - ... +) -> "WarningsChecker": ... @overload @@ -100,8 +97,7 @@ def warns( func: Callable[..., T], *args: Any, **kwargs: Any, -) -> T: - ... +) -> T: ... def warns( @@ -336,7 +332,9 @@ def found_str(): @staticmethod def _validate_message(wrn: Any) -> None: - if not isinstance(msg := wrn.message.args[0], str): + 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__})" ) From 8a40d9fd0fd6e7003840c493e2fe1929f7b041a9 Mon Sep 17 00:00:00 2001 From: Reagan Lee <96998476+reaganjlee@users.noreply.github.com> Date: Fri, 9 Feb 2024 18:46:41 -0800 Subject: [PATCH 6/6] formats --- src/_pytest/recwarn.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/_pytest/recwarn.py b/src/_pytest/recwarn.py index ec91bc4841b..707e90499de 100644 --- a/src/_pytest/recwarn.py +++ b/src/_pytest/recwarn.py @@ -42,11 +42,13 @@ 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( @@ -88,7 +90,8 @@ def warns( expected_warning: Union[Type[Warning], Tuple[Type[Warning], ...]] = ..., *, match: Optional[Union[str, Pattern[str]]] = ..., -) -> "WarningsChecker": ... +) -> "WarningsChecker": + ... @overload @@ -97,7 +100,8 @@ def warns( func: Callable[..., T], *args: Any, **kwargs: Any, -) -> T: ... +) -> T: + ... def warns(