Description
read more about causing / context exceptions in the python doc
What's the problem this feature will solve?
It is quite unintuitive to match a causing exceptions (raise Exception("i'm an exception") from Exception("I'm the causing exception")
) with pytest.
As for an example, imagine you want to match a ValueError with the message "i'm a causing exception"
def i_raise_a_causing_exception():
raise RuntimeError("i'm an exception") from ValueError("i'm a causing exception")
If you want to do that now, you have to use the workaround described below.
Describe the solution you'd like
pytest.raises
could have an option to match the causing/context exception. Of course, now comes the problem of recursivity since causing/context exceptions can themselves be causing exceptions.
This would allow us to do something along the lines of
def test_catch_cause_exception():
with pytest.raises(ValueError, match="i'm a causing exception", cause=True)
if we have multiple caused exception chained together, we could also have:
def i_raise_a_causing_exception():
try:
raise ValueError("i'm the 1st causing exception") from ValueError("i'm the 2nd causing exception")
except ValueError as e:
raise ValueError("i'm an exception") from e
def test_catch_cause_exception():
with pytest.raises(ValueError, match="i'm the 3rd causing exception", cause=2):
i_raise_a_causing_exception()
Of course, we would have the same thing for context exceptions:
def i_raise_a_context_exception():
try:
try:
raise ValueError("This is the first context")
finally:
raise ValueError("This is the second context")
finally:
raise ValueError("This is the cause")
def test_catch_context_exception():
with pytest.raises(ValueError, match="This is the second context", context=2):
i_raise_a_context_exception()
Alternative Solutions
I just found a workaround in https://stackoverflow.com/a/78939835/12550791 (disclaimer, i'm the author of the question and of the solution).
def test_catch_cause_exception():
# match any exception
with pytest.raises(Exception) as exc:
i_raise_a_causing_exception()
# match the cause exception
with pytest.raises(ValueError, match="i'm a causing exception"):
# in case the first exception caught does not have a cause.
# This will mark the test a failed with `did not raise`.
if exc.value.__cause__ is not None:
raise exc.value.__cause__
However, if you have multiple level of cause exceptions you will have to rely on recursivity or make one with the mess.