Raising Exceptions
Sometimes you might want to trigger an exception intentionally in your code. This is done with the raise statement. You raise exceptions to signal that an error condition has occurred that should be handled by the caller or at a higher level. For example, if a function gets an argument value that is invalid and it cannot continue, it can raise a ValueError to indicate this:
def square_root(x):
if x < 0:
raise ValueError(f"Cannot compute square root of negative number: {x}")
# continue with normal computation
import math
return math.sqrt(x)
In this function, we check for a negative input. Instead of returning an error code, we raise a ValueError with a descriptive message. The raise keyword can be used with either an exception instance or an exception class. For instance, raise ValueError("message") creates a ValueError exception with that message and raises it. If you use raise ValueError (providing a class), Python will instantiate it for you (essentially the same as raising ValueError() with no arguments).
When you raise an exception, normal execution stops and the exception propagates up to the nearest try/except that can handle that exception type. If none is found, it will terminate the program and show a traceback.
Creating custom exceptions
Python allows you to define your own exception types by creating a class that inherits from the built-in Exception class (or one of its subclasses). Custom exceptions are useful to represent errors that are specific to your application or domain, making them clearer and allowing callers to catch your specific errors separately if needed.
Creating a custom exception is straightforward:
class InvalidOperationError(Exception):
"""Custom exception for invalid operations."""
pass
This defines a new exception type InvalidOperationError. By convention, custom exception class names end in “Error” to make it obvious they’re exceptions. We inherit from Exception which is the base class for most regular exceptions. We don’t need to add any special behavior – often a simple pass is enough, unless you want to override the __str__ or add attributes.
You can now use raise InvalidOperationError("Detailed message") to throw this error. Anyone catching exceptions can catch it specifically with except InvalidOperationError:.
Example: Using a custom exception
class NegativeNumberError(Exception):
pass
def compute_log(x):
if x < 0:
raise NegativeNumberError("Cannot take logarithm of a negative number")
import math
return math.log(x)
try:
compute_log(-5)
except NegativeNumberError as e:
print("Error:", e)
In this example, compute_log(-5) will raise our NegativeNumberError. The except NegativeNumberError catches it and prints the error message. If we didn’t catch it, we’d see a traceback just like with built-in exceptions, and the type would be __main__.NegativeNumberError with our message.
Custom exceptions let you differentiate error causes. For instance, a function might raise NegativeNumberError for negatives, ZeroDivisionError for divide-by-zero (built-in), etc., and the caller can handle these separately.
Raising exceptions in except blocks (exception chaining)
If you catch an exception and decide to raise a different exception in response, Python will by default attach the original exception as context, so you don’t lose the traceback of what happened first. You can also explicitly chain exceptions with raise NewException(...) from exc. For example:
try:
... # some operation
except FileNotFoundError as e:
raise RuntimeError("Failed to process data") from e
This will raise a RuntimeError but the traceback will show that a FileNotFoundError was the direct cause. This can be useful to abstract lower-level errors into higher-level exceptions without losing the original info. If for some reason you want to suppress the context, you can raise from None, but that’s less common.
Key points:
-
Use
raiseto trigger exceptions when needed (like to enforce an error condition). -
Create custom exception classes by subclassing
Exceptionfor clearer, domain-specific errors. -
When raising exceptions in your code, include an informative message to make debugging easier.
-
Know that raising an exception interrupts function execution; any code after a
raisein that function won’t run unless the exception is caught somewhere above.
