Handle `match` with `pytest.raises()` (#6753)

Fixes https://github.com/pytest-dev/pytest/issues/6752.
This commit is contained in:
Daniel Hahler 2020-02-22 23:32:55 +01:00 committed by GitHub
parent 68fe0eb8f3
commit c8b4a1a471
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 32 additions and 9 deletions

View File

@ -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).

View File

@ -557,18 +557,16 @@ def raises( # noqa: F811
expected_exception: Union["Type[_E]", Tuple["Type[_E]", ...]],
func: Callable,
*args: Any,
match: Optional[str] = ...,
**kwargs: Any
) -> Optional[_pytest._code.ExceptionInfo[_E]]:
) -> _pytest._code.ExceptionInfo[_E]:
... # pragma: no cover
def raises( # noqa: F811
expected_exception: Union["Type[_E]", Tuple["Type[_E]", ...]],
*args: Any,
match: Optional[Union[str, "Pattern"]] = None,
**kwargs: Any
) -> Union["RaisesContext[_E]", Optional[_pytest._code.ExceptionInfo[_E]]]:
) -> Union["RaisesContext[_E]", _pytest._code.ExceptionInfo[_E]]:
r"""
Assert that a code block/function call raises ``expected_exception``
or raise a failure exception otherwise.
@ -579,8 +577,12 @@ def raises( # noqa: F811
string that may contain `special characters`__, the pattern can
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
@ -684,6 +686,7 @@ def raises( # noqa: F811
message = "DID NOT RAISE {}".format(expected_exception)
if not args:
match = kwargs.pop("match", None)
if kwargs:
msg = "Unexpected keyword arguments passed to pytest.raises: "
msg += ", ".join(sorted(kwargs))

View File

@ -1,3 +1,4 @@
import re
import sys
import pytest
@ -154,7 +155,7 @@ class TestRaises:
else:
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):
"""
Ensure pytest.raises does not leave a reference cycle (#1965).
@ -175,6 +176,8 @@ class TestRaises:
if method == "function":
pytest.raises(ValueError, t)
elif method == "function_match":
pytest.raises(ValueError, t).match("^$")
else:
with pytest.raises(ValueError):
t()
@ -184,7 +187,7 @@ class TestRaises:
assert refcount == len(gc.get_referrers(t))
def test_raises_match(self):
def test_raises_match(self) -> None:
msg = r"with base \d+"
with pytest.raises(ValueError, match=msg):
int("asdf")
@ -194,13 +197,27 @@ class TestRaises:
int("asdf")
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
)
with pytest.raises(AssertionError, match=expr):
with pytest.raises(AssertionError, match=re.escape(expr)):
with pytest.raises(ValueError, match=msg):
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):
with pytest.raises(AssertionError) as excinfo:
with pytest.raises(AssertionError, match="'foo"):