-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
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.