Handle `match` with `pytest.raises()` (#6753)
Fixes https://github.com/pytest-dev/pytest/issues/6752.
This commit is contained in:
parent
68fe0eb8f3
commit
c8b4a1a471
|
@ -0,0 +1,3 @@
|
||||||
|
When :py:func:`pytest.raises` is used as a function (as opposed to a context manager),
|
||||||
|
a `match` keyword argument is now passed through to the tested function. Previously
|
||||||
|
it was swallowed and ignored (regression in pytest 5.1.0).
|
|
@ -557,18 +557,16 @@ def raises( # noqa: F811
|
||||||
expected_exception: Union["Type[_E]", Tuple["Type[_E]", ...]],
|
expected_exception: Union["Type[_E]", Tuple["Type[_E]", ...]],
|
||||||
func: Callable,
|
func: Callable,
|
||||||
*args: Any,
|
*args: Any,
|
||||||
match: Optional[str] = ...,
|
|
||||||
**kwargs: Any
|
**kwargs: Any
|
||||||
) -> Optional[_pytest._code.ExceptionInfo[_E]]:
|
) -> _pytest._code.ExceptionInfo[_E]:
|
||||||
... # pragma: no cover
|
... # pragma: no cover
|
||||||
|
|
||||||
|
|
||||||
def raises( # noqa: F811
|
def raises( # noqa: F811
|
||||||
expected_exception: Union["Type[_E]", Tuple["Type[_E]", ...]],
|
expected_exception: Union["Type[_E]", Tuple["Type[_E]", ...]],
|
||||||
*args: Any,
|
*args: Any,
|
||||||
match: Optional[Union[str, "Pattern"]] = None,
|
|
||||||
**kwargs: Any
|
**kwargs: Any
|
||||||
) -> Union["RaisesContext[_E]", Optional[_pytest._code.ExceptionInfo[_E]]]:
|
) -> Union["RaisesContext[_E]", _pytest._code.ExceptionInfo[_E]]:
|
||||||
r"""
|
r"""
|
||||||
Assert that a code block/function call raises ``expected_exception``
|
Assert that a code block/function call raises ``expected_exception``
|
||||||
or raise a failure exception otherwise.
|
or raise a failure exception otherwise.
|
||||||
|
@ -579,8 +577,12 @@ def raises( # noqa: F811
|
||||||
string that may contain `special characters`__, the pattern can
|
string that may contain `special characters`__, the pattern can
|
||||||
first be escaped with ``re.escape``.
|
first be escaped with ``re.escape``.
|
||||||
|
|
||||||
__ https://docs.python.org/3/library/re.html#regular-expression-syntax
|
(This is only used when ``pytest.raises`` is used as a context manager,
|
||||||
|
and passed through to the function otherwise.
|
||||||
|
When using ``pytest.raises`` as a function, you can use:
|
||||||
|
``pytest.raises(Exc, func, match="passed on").match("my pattern")``.)
|
||||||
|
|
||||||
|
__ https://docs.python.org/3/library/re.html#regular-expression-syntax
|
||||||
|
|
||||||
.. currentmodule:: _pytest._code
|
.. currentmodule:: _pytest._code
|
||||||
|
|
||||||
|
@ -684,6 +686,7 @@ def raises( # noqa: F811
|
||||||
message = "DID NOT RAISE {}".format(expected_exception)
|
message = "DID NOT RAISE {}".format(expected_exception)
|
||||||
|
|
||||||
if not args:
|
if not args:
|
||||||
|
match = kwargs.pop("match", None)
|
||||||
if kwargs:
|
if kwargs:
|
||||||
msg = "Unexpected keyword arguments passed to pytest.raises: "
|
msg = "Unexpected keyword arguments passed to pytest.raises: "
|
||||||
msg += ", ".join(sorted(kwargs))
|
msg += ", ".join(sorted(kwargs))
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -154,7 +155,7 @@ class TestRaises:
|
||||||
else:
|
else:
|
||||||
assert False, "Expected pytest.raises.Exception"
|
assert False, "Expected pytest.raises.Exception"
|
||||||
|
|
||||||
@pytest.mark.parametrize("method", ["function", "with"])
|
@pytest.mark.parametrize("method", ["function", "function_match", "with"])
|
||||||
def test_raises_cyclic_reference(self, method):
|
def test_raises_cyclic_reference(self, method):
|
||||||
"""
|
"""
|
||||||
Ensure pytest.raises does not leave a reference cycle (#1965).
|
Ensure pytest.raises does not leave a reference cycle (#1965).
|
||||||
|
@ -175,6 +176,8 @@ class TestRaises:
|
||||||
|
|
||||||
if method == "function":
|
if method == "function":
|
||||||
pytest.raises(ValueError, t)
|
pytest.raises(ValueError, t)
|
||||||
|
elif method == "function_match":
|
||||||
|
pytest.raises(ValueError, t).match("^$")
|
||||||
else:
|
else:
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
t()
|
t()
|
||||||
|
@ -184,7 +187,7 @@ class TestRaises:
|
||||||
|
|
||||||
assert refcount == len(gc.get_referrers(t))
|
assert refcount == len(gc.get_referrers(t))
|
||||||
|
|
||||||
def test_raises_match(self):
|
def test_raises_match(self) -> None:
|
||||||
msg = r"with base \d+"
|
msg = r"with base \d+"
|
||||||
with pytest.raises(ValueError, match=msg):
|
with pytest.raises(ValueError, match=msg):
|
||||||
int("asdf")
|
int("asdf")
|
||||||
|
@ -194,13 +197,27 @@ class TestRaises:
|
||||||
int("asdf")
|
int("asdf")
|
||||||
|
|
||||||
msg = "with base 16"
|
msg = "with base 16"
|
||||||
expr = r"Pattern '{}' does not match \"invalid literal for int\(\) with base 10: 'asdf'\"".format(
|
expr = "Pattern {!r} does not match \"invalid literal for int() with base 10: 'asdf'\"".format(
|
||||||
msg
|
msg
|
||||||
)
|
)
|
||||||
with pytest.raises(AssertionError, match=expr):
|
with pytest.raises(AssertionError, match=re.escape(expr)):
|
||||||
with pytest.raises(ValueError, match=msg):
|
with pytest.raises(ValueError, match=msg):
|
||||||
int("asdf", base=10)
|
int("asdf", base=10)
|
||||||
|
|
||||||
|
# "match" without context manager.
|
||||||
|
pytest.raises(ValueError, int, "asdf").match("invalid literal")
|
||||||
|
with pytest.raises(AssertionError) as excinfo:
|
||||||
|
pytest.raises(ValueError, int, "asdf").match(msg)
|
||||||
|
assert str(excinfo.value) == expr
|
||||||
|
|
||||||
|
pytest.raises(TypeError, int, match="invalid")
|
||||||
|
|
||||||
|
def tfunc(match):
|
||||||
|
raise ValueError("match={}".format(match))
|
||||||
|
|
||||||
|
pytest.raises(ValueError, tfunc, match="asdf").match("match=asdf")
|
||||||
|
pytest.raises(ValueError, tfunc, match="").match("match=")
|
||||||
|
|
||||||
def test_match_failure_string_quoting(self):
|
def test_match_failure_string_quoting(self):
|
||||||
with pytest.raises(AssertionError) as excinfo:
|
with pytest.raises(AssertionError) as excinfo:
|
||||||
with pytest.raises(AssertionError, match="'foo"):
|
with pytest.raises(AssertionError, match="'foo"):
|
||||||
|
|
Loading…
Reference in New Issue