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