Raise From
Raise From
Section titled βRaise FromβWhat it is
Section titled βWhat it isβraise NewException(...) from original_exception (Python 3.3+) explicitly links a new exception to the one that caused it. Python also does this implicitly β any exception raised while handling another exception gets chained automatically β but raise ... from ... lets you control and clarify that link, including suppressing it entirely.
Before this feature
Section titled βBefore this featureβWithout chaining, converting a low-level exception into a higher-level, more meaningful one throws away the original cause:
def load_config(path): try: with open(path) as f: return f.read() except FileNotFoundError: raise ValueError(f"Config not found: {path}")
# Traceback shows only ValueError β the original FileNotFoundError and# its details (which file, which line) are gone from the visible chain# unless you're on 3.3+, where it's captured automatically anyway.After this feature
Section titled βAfter this featureβdef load_config(path): try: with open(path) as f: return f.read() except FileNotFoundError as e: raise ValueError(f"Config not found: {path}") from e
# Traceback now shows both:# FileNotFoundError: [Errno 2] No such file or directory: 'config.yml'## The above exception was the direct cause of the following exception:## ValueError: Config not found: config.yml# Suppressing the chain entirely when the original truly isn't useful contexttry: risky_operation()except SomeError: raise CleanError("Something went wrong") from None# Traceback shows only CleanError β no "During handling of..." noiseWhy this is better
Section titled βWhy this is betterβ- Full debugging context: the traceback shows both exceptions β what ultimately went wrong and what originally triggered it β instead of losing the root cause.
- Explicit intent:
from esays βthis new exception is because of eβ (sets__cause__);from Nonesays βdeliberately donβt show a cause,β both clearer than accidental chaining. - Distinguishes deliberate vs. accidental: a bug that raises a second, unrelated exception while handling the first still shows up in the traceback (
__context__) even withoutfrom, which is often exactly what you want when debugging β you see that something also broke in yourexceptblock.
Key notes / edge cases
Section titled βKey notes / edge casesβraise X from YsetsX.__cause__ = Yand prints βThe above exception was the direct cause of the following exception.β- Without
from, an exception raised inside anexceptblock still gets chained automatically via__context__, printed as βDuring handling of the above exception, another exception occurred.β raise X from Noneexplicitly suppresses the chain display (sets__suppress_context__ = True) β useful when the original exception is an implementation detail that would just confuse the caller.__cause__(explicit, viafrom) and__context__(implicit, automatic) are different attributes βfromalways takes precedence for whatβs displayed.
Quick practice
Section titled βQuick practiceβ-
What does
raise ValueError("bad config") from eset on the new exception?Answer
`__cause__` is set to `e`, and the traceback shows `e` as "the direct cause of" the new `ValueError`. -
If you
raisea new exception inside anexceptblock without usingfrom, is the original exception lost?Answer
No β it's still shown, via the implicit `__context__` chain ("During handling of the above exception, another exception occurred"), just without the explicit "direct cause" framing. -
How do you suppress the chained-exception traceback entirely?
Answer
`raise NewException(...) from None`.