Catch any warning on warns with no arg passed

This commit is contained in:
Olga Matoula 2021-05-15 15:15:43 +01:00
parent c516dba69a
commit 8b2f83772d
8 changed files with 45 additions and 11 deletions

View File

@ -231,6 +231,7 @@ Nicholas Murphy
Niclas Olofsson Niclas Olofsson
Nicolas Delaby Nicolas Delaby
Nikolay Kondratyev Nikolay Kondratyev
Olga Matoula
Oleg Pidsadnyi Oleg Pidsadnyi
Oleg Sushchenko Oleg Sushchenko
Oliver Bestwalter Oliver Bestwalter

View File

@ -0,0 +1,4 @@
Reducing confusion from `pytest.warns(None)` by:
- Allowing no arguments to be passed in order to catch any exception (no argument defaults to `Warning`).
- Emit a deprecation warning if passed `None`.

View File

@ -332,11 +332,11 @@ You can record raised warnings either using func:`pytest.warns` or with
the ``recwarn`` fixture. the ``recwarn`` fixture.
To record with func:`pytest.warns` without asserting anything about the warnings, To record with func:`pytest.warns` without asserting anything about the warnings,
pass ``None`` as the expected warning type: pass no arguments as the expected warning type and it will default to a generic Warning:
.. code-block:: python .. code-block:: python
with pytest.warns(None) as record: with pytest.warns() as record:
warnings.warn("user", UserWarning) warnings.warn("user", UserWarning)
warnings.warn("runtime", RuntimeWarning) warnings.warn("runtime", RuntimeWarning)

View File

@ -101,6 +101,11 @@ HOOK_LEGACY_PATH_ARG = UnformattedWarning(
"see https://docs.pytest.org/en/latest/deprecations.html" "see https://docs.pytest.org/en/latest/deprecations.html"
"#py-path-local-arguments-for-hooks-replaced-with-pathlib-path", "#py-path-local-arguments-for-hooks-replaced-with-pathlib-path",
) )
WARNS_NONE_ARG = PytestDeprecationWarning(
"Please pass an explicit Warning type or tuple of Warning types."
)
# You want to make some `__init__` or function "private". # You want to make some `__init__` or function "private".
# #
# def my_private_function(some, args): # def my_private_function(some, args):

View File

@ -17,6 +17,7 @@ from typing import Union
from _pytest.compat import final from _pytest.compat import final
from _pytest.deprecated import check_ispytest from _pytest.deprecated import check_ispytest
from _pytest.deprecated import WARNS_NONE_ARG
from _pytest.fixtures import fixture from _pytest.fixtures import fixture
from _pytest.outcomes import fail from _pytest.outcomes import fail
@ -83,7 +84,7 @@ def deprecated_call(
@overload @overload
def warns( def warns(
expected_warning: Optional[Union[Type[Warning], Tuple[Type[Warning], ...]]], expected_warning: Optional[Union[Type[Warning], Tuple[Type[Warning], ...]]] = ...,
*, *,
match: Optional[Union[str, Pattern[str]]] = ..., match: Optional[Union[str, Pattern[str]]] = ...,
) -> "WarningsChecker": ) -> "WarningsChecker":
@ -101,7 +102,7 @@ def warns(
def warns( def warns(
expected_warning: Optional[Union[Type[Warning], Tuple[Type[Warning], ...]]], expected_warning: Optional[Union[Type[Warning], Tuple[Type[Warning], ...]]] = Warning,
*args: Any, *args: Any,
match: Optional[Union[str, Pattern[str]]] = None, match: Optional[Union[str, Pattern[str]]] = None,
**kwargs: Any, **kwargs: Any,
@ -232,7 +233,7 @@ class WarningsChecker(WarningsRecorder):
self, self,
expected_warning: Optional[ expected_warning: Optional[
Union[Type[Warning], Tuple[Type[Warning], ...]] Union[Type[Warning], Tuple[Type[Warning], ...]]
] = None, ] = Warning,
match_expr: Optional[Union[str, Pattern[str]]] = None, match_expr: Optional[Union[str, Pattern[str]]] = None,
*, *,
_ispytest: bool = False, _ispytest: bool = False,
@ -242,6 +243,7 @@ class WarningsChecker(WarningsRecorder):
msg = "exceptions must be derived from Warning, not %s" msg = "exceptions must be derived from Warning, not %s"
if expected_warning is None: if expected_warning is None:
warnings.warn(WARNS_NONE_ARG, stacklevel=4)
expected_warning_tup = None expected_warning_tup = None
elif isinstance(expected_warning, tuple): elif isinstance(expected_warning, tuple):
for exc in expected_warning: for exc in expected_warning:

View File

@ -178,3 +178,11 @@ def test_hookproxy_warnings_for_fspath(tmp_path, hooktype, request):
assert l1 < record.lineno < l2 assert l1 < record.lineno < l2
hooks.pytest_ignore_collect(config=request.config, fspath=tmp_path) hooks.pytest_ignore_collect(config=request.config, fspath=tmp_path)
def test_warns_none_is_deprecated():
with pytest.warns(
PytestDeprecationWarning,
match="Please pass an explicit Warning type or tuple of Warning types.",
):
pytest.warns(None)

View File

@ -298,7 +298,7 @@ class TestWarns:
assert str(record[0].message) == "user" assert str(record[0].message) == "user"
def test_record_only(self) -> None: def test_record_only(self) -> None:
with pytest.warns(None) as record: with pytest.warns() as record:
warnings.warn("user", UserWarning) warnings.warn("user", UserWarning)
warnings.warn("runtime", RuntimeWarning) warnings.warn("runtime", RuntimeWarning)
@ -306,6 +306,17 @@ class TestWarns:
assert str(record[0].message) == "user" assert str(record[0].message) == "user"
assert str(record[1].message) == "runtime" assert str(record[1].message) == "runtime"
def test_record_only_none_deprecated_warn(self) -> None:
with warnings.catch_warnings():
warnings.simplefilter("ignore")
with pytest.warns(None) as record:
warnings.warn("user", UserWarning)
warnings.warn("runtime", RuntimeWarning)
assert len(record) == 2
assert str(record[0].message) == "user"
assert str(record[1].message) == "runtime"
def test_record_by_subclass(self) -> None: def test_record_by_subclass(self) -> None:
with pytest.warns(Warning) as record: with pytest.warns(Warning) as record:
warnings.warn("user", UserWarning) warnings.warn("user", UserWarning)

View File

@ -1,6 +1,7 @@
import os import os
import stat import stat
import sys import sys
import warnings
from pathlib import Path from pathlib import Path
from typing import Callable from typing import Callable
from typing import cast from typing import cast
@ -400,11 +401,13 @@ class TestRmRf:
assert fn.is_file() assert fn.is_file()
# ignored function # ignored function
with pytest.warns(None) as warninfo: with warnings.catch_warnings():
exc_info4 = (None, PermissionError(), None) warnings.simplefilter("ignore")
on_rm_rf_error(os.open, str(fn), exc_info4, start_path=tmp_path) with pytest.warns(None) as warninfo:
assert fn.is_file() exc_info4 = (None, PermissionError(), None)
assert not [x.message for x in warninfo] on_rm_rf_error(os.open, str(fn), exc_info4, start_path=tmp_path)
assert fn.is_file()
assert not [x.message for x in warninfo]
exc_info5 = (None, PermissionError(), None) exc_info5 = (None, PermissionError(), None)
on_rm_rf_error(os.unlink, str(fn), exc_info5, start_path=tmp_path) on_rm_rf_error(os.unlink, str(fn), exc_info5, start_path=tmp_path)