Skip to content

Commit 8b2f837

Browse files
committed
Catch any warning on warns with no arg passed
1 parent c516dba commit 8b2f837

File tree

8 files changed

+45
-11
lines changed

8 files changed

+45
-11
lines changed

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ Nicholas Murphy
231231
Niclas Olofsson
232232
Nicolas Delaby
233233
Nikolay Kondratyev
234+
Olga Matoula
234235
Oleg Pidsadnyi
235236
Oleg Sushchenko
236237
Oliver Bestwalter

changelog/8645.improvement.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Reducing confusion from `pytest.warns(None)` by:
2+
3+
- Allowing no arguments to be passed in order to catch any exception (no argument defaults to `Warning`).
4+
- Emit a deprecation warning if passed `None`.

doc/en/how-to/capture-warnings.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -332,11 +332,11 @@ You can record raised warnings either using func:`pytest.warns` or with
332332
the ``recwarn`` fixture.
333333

334334
To record with func:`pytest.warns` without asserting anything about the warnings,
335-
pass ``None`` as the expected warning type:
335+
pass no arguments as the expected warning type and it will default to a generic Warning:
336336

337337
.. code-block:: python
338338
339-
with pytest.warns(None) as record:
339+
with pytest.warns() as record:
340340
warnings.warn("user", UserWarning)
341341
warnings.warn("runtime", RuntimeWarning)
342342

src/_pytest/deprecated.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@
101101
"see https://docs.pytest.org/en/latest/deprecations.html"
102102
"#py-path-local-arguments-for-hooks-replaced-with-pathlib-path",
103103
)
104+
105+
WARNS_NONE_ARG = PytestDeprecationWarning(
106+
"Please pass an explicit Warning type or tuple of Warning types."
107+
)
108+
104109
# You want to make some `__init__` or function "private".
105110
#
106111
# def my_private_function(some, args):

src/_pytest/recwarn.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
from _pytest.compat import final
1919
from _pytest.deprecated import check_ispytest
20+
from _pytest.deprecated import WARNS_NONE_ARG
2021
from _pytest.fixtures import fixture
2122
from _pytest.outcomes import fail
2223

@@ -83,7 +84,7 @@ def deprecated_call(
8384

8485
@overload
8586
def warns(
86-
expected_warning: Optional[Union[Type[Warning], Tuple[Type[Warning], ...]]],
87+
expected_warning: Optional[Union[Type[Warning], Tuple[Type[Warning], ...]]] = ...,
8788
*,
8889
match: Optional[Union[str, Pattern[str]]] = ...,
8990
) -> "WarningsChecker":
@@ -101,7 +102,7 @@ def warns(
101102

102103

103104
def warns(
104-
expected_warning: Optional[Union[Type[Warning], Tuple[Type[Warning], ...]]],
105+
expected_warning: Optional[Union[Type[Warning], Tuple[Type[Warning], ...]]] = Warning,
105106
*args: Any,
106107
match: Optional[Union[str, Pattern[str]]] = None,
107108
**kwargs: Any,
@@ -232,7 +233,7 @@ def __init__(
232233
self,
233234
expected_warning: Optional[
234235
Union[Type[Warning], Tuple[Type[Warning], ...]]
235-
] = None,
236+
] = Warning,
236237
match_expr: Optional[Union[str, Pattern[str]]] = None,
237238
*,
238239
_ispytest: bool = False,
@@ -242,6 +243,7 @@ def __init__(
242243

243244
msg = "exceptions must be derived from Warning, not %s"
244245
if expected_warning is None:
246+
warnings.warn(WARNS_NONE_ARG, stacklevel=4)
245247
expected_warning_tup = None
246248
elif isinstance(expected_warning, tuple):
247249
for exc in expected_warning:

testing/deprecated_test.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,3 +178,11 @@ def test_hookproxy_warnings_for_fspath(tmp_path, hooktype, request):
178178
assert l1 < record.lineno < l2
179179

180180
hooks.pytest_ignore_collect(config=request.config, fspath=tmp_path)
181+
182+
183+
def test_warns_none_is_deprecated():
184+
with pytest.warns(
185+
PytestDeprecationWarning,
186+
match="Please pass an explicit Warning type or tuple of Warning types.",
187+
):
188+
pytest.warns(None)

testing/test_recwarn.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,14 +298,25 @@ def test_record(self) -> None:
298298
assert str(record[0].message) == "user"
299299

300300
def test_record_only(self) -> None:
301-
with pytest.warns(None) as record:
301+
with pytest.warns() as record:
302302
warnings.warn("user", UserWarning)
303303
warnings.warn("runtime", RuntimeWarning)
304304

305305
assert len(record) == 2
306306
assert str(record[0].message) == "user"
307307
assert str(record[1].message) == "runtime"
308308

309+
def test_record_only_none_deprecated_warn(self) -> None:
310+
with warnings.catch_warnings():
311+
warnings.simplefilter("ignore")
312+
with pytest.warns(None) as record:
313+
warnings.warn("user", UserWarning)
314+
warnings.warn("runtime", RuntimeWarning)
315+
316+
assert len(record) == 2
317+
assert str(record[0].message) == "user"
318+
assert str(record[1].message) == "runtime"
319+
309320
def test_record_by_subclass(self) -> None:
310321
with pytest.warns(Warning) as record:
311322
warnings.warn("user", UserWarning)

testing/test_tmpdir.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import os
22
import stat
33
import sys
4+
import warnings
45
from pathlib import Path
56
from typing import Callable
67
from typing import cast
@@ -400,11 +401,13 @@ def test_on_rm_rf_error(self, tmp_path: Path) -> None:
400401
assert fn.is_file()
401402

402403
# ignored function
403-
with pytest.warns(None) as warninfo:
404-
exc_info4 = (None, PermissionError(), None)
405-
on_rm_rf_error(os.open, str(fn), exc_info4, start_path=tmp_path)
406-
assert fn.is_file()
407-
assert not [x.message for x in warninfo]
404+
with warnings.catch_warnings():
405+
warnings.simplefilter("ignore")
406+
with pytest.warns(None) as warninfo:
407+
exc_info4 = (None, PermissionError(), None)
408+
on_rm_rf_error(os.open, str(fn), exc_info4, start_path=tmp_path)
409+
assert fn.is_file()
410+
assert not [x.message for x in warninfo]
408411

409412
exc_info5 = (None, PermissionError(), None)
410413
on_rm_rf_error(os.unlink, str(fn), exc_info5, start_path=tmp_path)

0 commit comments

Comments
 (0)