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