Raising and Customizing Exceptions
Overview
Sometimes you'll need to trigger your own exceptions to signal errors in logic or input. You'll learn to use raise and define custom exception classes that make your code more robust and predictable, creating clear error communication.
What You Will Learn in This Lesson
By the end of this lesson, you will know:
- Raising exceptions: Use
raiseto trigger your own exceptions. - When to raise: Understand when it's appropriate to raise exceptions.
- Custom exceptions: Create your own exception classes for specific errors.
- Error messages: Provide helpful error messages with your exceptions.
- Best practices: Write clear, informative exceptions.
Raising Exceptions with raise
Sometimes you want to trigger an exception yourself when something goes wrong. You can do this with the raise keyword.
raise Exception("Something went wrong!")
def divide(a, b):
if b == 0:
raise ValueError("Cannot divide by zero!")
return a / b
result = divide(10, 0)
# ValueError: Cannot divide by zero!
When to Raise Exceptions
- When input is invalid
- When a required condition isn't met
- When something unexpected happens
- To signal errors to calling code
Raising Specific Exceptions
You can raise any built-in exception type:
# Raise ValueError for invalid input
if age < 0:
raise ValueError("Age cannot be negative!")
# Raise FileNotFoundError
if not os.path.exists(filename):
raise FileNotFoundError(f"File '{filename}' not found!")
# Raise TypeError
if not isinstance(value, int):
raise TypeError("Expected an integer!")
Creating Custom Exceptions
You can create your own exception classes for specific situations. This makes your code clearer and easier to debug.
class InvalidAgeError(Exception):
"""Raised when age is invalid"""
pass
# Using the custom exception
def set_age(age):
if age < 0 or age > 150:
raise InvalidAgeError(f"Age {age} is not valid!")
return age
Custom Exception Benefits
Custom exceptions make it clear what went wrong and allow callers to catch specific error types.
class FileFormatError(Exception):
"""Raised when file format is incorrect"""
def __init__(self, message, filename):
self.message = message
self.filename = filename
super().__init__(self.message)
# Using it
raise FileFormatError("Invalid format", "data.txt")
Raising Exceptions in Functions
Functions often raise exceptions to signal errors to the caller:
def read_config(filename):
if not filename.endswith('.txt'):
raise ValueError("Config file must be .txt format")
try:
with open(filename, 'r') as file:
return file.read()
except FileNotFoundError:
raise FileNotFoundError(f"Config file '{filename}' not found!")
# Using the function
try:
config = read_config("settings.txt")
except ValueError as e:
print(f"Invalid input: {e}")
except FileNotFoundError as e:
print(f"File error: {e}")
Re-raising Exceptions
Sometimes you want to catch an exception, do something, and then re-raise it:
try:
file = open("data.txt", "r")
content = file.read()
except FileNotFoundError:
print("Logging: File not found")
raise # Re-raise the exception
Use case: You might want to log an error before letting it propagate to the caller.
Practice: Raising Exceptions
Try It YourselfTry creating and raising your own exceptions:
What happened? The function validated the age and raised a ValueError with a helpful message when the age was invalid.
Advanced Custom Exception Examples
Here are more examples of custom exceptions for different scenarios:
class ValidationError(Exception):
"""Raised when validation fails"""
def __init__(self, message, field_name, value):
self.message = message
self.field_name = field_name
self.value = value
super().__init__(self.message)
def __str__(self):
return f"{self.message} (Field: {self.field_name}, Value: {self.value})"
# Usage
def validate_email(email):
if '@' not in email:
raise ValidationError("Invalid email format", "email", email)
return email
try:
validate_email("notanemail")
except ValidationError as e:
print(e) # Prints: Invalid email format (Field: email, Value: notanemail)
class FileError(Exception):
"""Base exception for file-related errors"""
pass
class FileNotFoundError(FileError):
"""Raised when file doesn't exist"""
pass
class FilePermissionError(FileError):
"""Raised when file permission is denied"""
pass
class FileFormatError(FileError):
"""Raised when file format is invalid"""
pass
# You can catch the base exception to catch all file errors
try:
# Some file operation
pass
except FileError as e:
print(f"File error occurred: {e}")
Exception Inheritance
Creating exception hierarchies allows you to:
- Catch related exceptions together using the base class
- Organize exceptions logically
- Provide more specific error handling
- Make code more maintainable
Best Practices
When raising exceptions, follow these guidelines:
Use Appropriate Types
Raise the most specific exception type that fits the error. Use built-in exceptions when they fit, create custom ones when needed.
Clear Messages
Provide helpful error messages that explain what went wrong and how to fix it. Include relevant context like filenames or values.
Document Exceptions
Document what exceptions your functions might raise in docstrings. This helps other developers (and yourself) understand error handling.
Don't Overuse Exceptions
Exceptions are for exceptional cases. Don't use them for normal control flow. Use return values or status codes for expected conditions.
def process_config_file(filename):
"""
Load and process a configuration file.
Args:
filename: Path to the configuration file
Returns:
dict: Configuration dictionary
Raises:
FileNotFoundError: If the config file doesn't exist
ValueError: If the file format is invalid
PermissionError: If file cannot be read
Example:
>>> config = process_config_file("settings.json")
>>> print(config['host'])
"""
# Implementation here
pass
Summary
In this lesson, you learned:
- raise: Trigger exceptions yourself when errors occur
- Specific exceptions: Raise appropriate exception types
- Custom exceptions: Create your own exception classes for clarity
- Re-raising: Catch, handle, and re-raise exceptions when needed
- Best practices: Use clear messages and appropriate types
Remember
Raising exceptions is a way to communicate errors. Make sure your error messages are clear and helpful!
End-of-Lesson Exercises
Think about these questions to reinforce what you've learned:
Exercise 1: When to Raise
When should you raise an exception in your code? Give an example of a function that would benefit from raising exceptions.
Exercise 2: Custom Exceptions
Why might you want to create a custom exception class instead of using a built-in one?