Merge pull request #8677 from olgarithms/warns-no-arg-catches-any
This commit is contained in:
commit
c198a7a67e
1
AUTHORS
1
AUTHORS
|
@ -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
|
||||||
|
|
|
@ -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`.
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -101,6 +101,12 @@ 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(
|
||||||
|
"Passing None to catch any warning has been deprecated, pass no arguments instead:\n"
|
||||||
|
" Replace pytest.warns(None) by simply pytest.warns()."
|
||||||
|
)
|
||||||
|
|
||||||
# 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):
|
||||||
|
|
|
@ -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: Union[Type[Warning], Tuple[Type[Warning], ...]] = ...,
|
||||||
*,
|
*,
|
||||||
match: Optional[Union[str, Pattern[str]]] = ...,
|
match: Optional[Union[str, Pattern[str]]] = ...,
|
||||||
) -> "WarningsChecker":
|
) -> "WarningsChecker":
|
||||||
|
@ -92,7 +93,7 @@ def warns(
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def warns(
|
def warns(
|
||||||
expected_warning: Optional[Union[Type[Warning], Tuple[Type[Warning], ...]]],
|
expected_warning: Union[Type[Warning], Tuple[Type[Warning], ...]],
|
||||||
func: Callable[..., T],
|
func: Callable[..., T],
|
||||||
*args: Any,
|
*args: Any,
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
|
@ -101,7 +102,7 @@ def warns(
|
||||||
|
|
||||||
|
|
||||||
def warns(
|
def warns(
|
||||||
expected_warning: Optional[Union[Type[Warning], Tuple[Type[Warning], ...]]],
|
expected_warning: 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:
|
||||||
|
|
|
@ -178,3 +178,15 @@ 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=re.escape(
|
||||||
|
"Passing None to catch any warning has been deprecated, pass no arguments instead:\n "
|
||||||
|
"Replace pytest.warns(None) by simply pytest.warns()."
|
||||||
|
),
|
||||||
|
):
|
||||||
|
with pytest.warns(None): # type: ignore[call-overload]
|
||||||
|
pass
|
||||||
|
|
|
@ -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,18 @@ 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:
|
||||||
|
# This should become an error when WARNS_NONE_ARG is removed in Pytest 7.0
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.simplefilter("ignore")
|
||||||
|
with pytest.warns(None) as record: # type: ignore[call-overload]
|
||||||
|
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)
|
||||||
|
|
|
@ -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: # type: ignore[call-overload]
|
||||||
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)
|
||||||
|
|
Loading…
Reference in New Issue