Skip to main content

Raising Exceptions: The `raise` Keyword

We have learned how to handle exceptions that Python raises automatically, like ValueError or FileNotFoundError. But what if you encounter an error condition in your own code that isn't covered by a built-in exception?

For example, what if a function requires a positive number, and the user provides a negative one? This isn't a TypeError or a ValueError in the traditional sense, but it's an invalid state for your specific logic. In these cases, you need to signal that an error has occurred yourself. This is done with the raise keyword.


📚 Prerequisites

You should understand what an exception is and how to handle one with a try...except block.


🎯 Article Outline: What You'll Master

In this article, you will learn:

  • The raise Keyword: The basic syntax for raising an exception.
  • Raising Built-in Exceptions: How to reuse Python's existing exceptions for common error conditions.
  • Creating Custom Exceptions: How to define your own, more descriptive exception classes.
  • Best Practices: When and how to raise exceptions to write clear, robust, and maintainable functions.

🧠 Section 1: The raise Keyword

The raise keyword allows you to manually trigger an exception at any point in your code. When Python encounters a raise statement, it stops the program's normal execution and starts the exception handling process, looking for an except block that matches the type of exception raised.

The Syntax:

raise ExceptionType("A descriptive error message")

You can raise any object that inherits from the base Exception class.


💻 Section 2: Raising Built-in Exceptions

The simplest way to signal an error is to raise one of Python's built-in exceptions. You should always do this when a built-in exception accurately describes your error. This makes your code more consistent with the rest of the Python ecosystem.

The most common one to raise manually is ValueError, which is appropriate when an argument has the right type but an invalid value for your function's logic.

Example: Validating Function Arguments Let's create a function to calculate a shipping cost, which cannot be negative.

# raising_builtin_exception.py

def calculate_shipping(weight_kg: float):
"""Calculates shipping cost. Weight cannot be zero or negative."""
if weight_kg <= 0:
# This is an invalid value for this function's logic.
raise ValueError("Weight must be a positive number.")

# A simple calculation for the example
return weight_kg * 2.50

# --- Let's test it ---
try:
cost = calculate_shipping(10)
print(f"Shipping cost for 10kg: ${cost:.2f}")

# This call will trigger the exception
cost_invalid = calculate_shipping(-5)
print(f"Shipping cost for -5kg: ${cost_invalid:.2f}")

except ValueError as e:
print(f"Error: {e}")

Output:

Shipping cost for 10kg: $25.00
Error: Weight must be a positive number.

By raising a ValueError, our function can clearly signal to the calling code that it was given an inappropriate argument, which can then be handled by a try...except block.


🛠️ Section 3: Creating Custom Exceptions

Sometimes, a built-in exception isn't specific enough. You might want to create your own exception types to represent errors that are unique to your application's domain. This makes your error handling code much more precise and readable.

To create a custom exception, you simply define a new class that inherits from the base Exception class.

Example: A Custom Banking Error Imagine we're writing a simple banking application. A user trying to withdraw more money than they have is a very specific error condition. A ValueError doesn't quite capture it. Let's create an InsufficientFundsError.

# custom_exception.py

# 1. Define the custom exception class
class InsufficientFundsError(Exception):
"""Raised when a withdrawal attempt exceeds the account balance."""
pass # We don't need any custom logic in it for now.

# 2. Create a function that uses it
def withdraw(balance: float, amount: float) -> float:
"""Withdraws an amount from a balance."""
if amount > balance:
# Raise our custom, descriptive exception
raise InsufficientFundsError(f"Cannot withdraw ${amount:.2f}. Current balance is only ${balance:.2f}.")

print("Withdrawal successful.")
return balance - amount

# --- Let's test it ---
my_balance = 100.00

try:
print(f"Balance: ${my_balance:.2f}")
my_balance = withdraw(my_balance, 50.00) # This will succeed
print(f"New Balance: ${my_balance:.2f}")

# This will fail
my_balance = withdraw(my_balance, 75.00)

except InsufficientFundsError as e:
print(f"Transaction failed: {e}")

Output:

Balance: $100.00
Withdrawal successful.
New Balance: $50.00
Transaction failed: Cannot withdraw $75.00. Current balance is only $50.00.

Now, our except block can specifically catch InsufficientFundsError, making it clear exactly what kind of error we are handling. We could have other except blocks for TypeError or other issues, and they wouldn't get mixed up.


✨ Conclusion & Key Takeaways

This article concludes our chapter on File I/O and Exception Handling! Knowing how to raise exceptions is just as important as knowing how to handle them. It allows you to enforce rules in your own functions and create clear, specific error conditions that make your applications more robust and easier to debug.

Let's summarize the key takeaways:

  • Use raise to Signal Errors: The raise keyword manually triggers an exception.
  • Reuse Built-in Exceptions: If a built-in exception like ValueError or TypeError fits your situation, use it.
  • Create Custom Exceptions for Clarity: For application-specific errors (like InsufficientFundsError), create your own exception classes by inheriting from Exception. This makes your code self-documenting.
  • Provide Clear Error Messages: The string you pass to an exception is crucial for helping developers understand what went wrong.

Challenge Yourself: Write a function that takes a dictionary as an argument. The function should check if the dictionary contains the keys 'name' and 'age'. If either key is missing, the function should raise a KeyError with a descriptive message.


➡️ Next Steps

Congratulations on completing Chapter 2! You've built a powerful foundation in Python, covering everything from functions and modules to file handling and error management.

In our next chapter, "Object-Oriented and Advanced Python," we will embark on a new journey to understand one of the most powerful paradigms in programming: Object-Oriented Programming (OOP). We'll start with the basics of classes and objects.

Happy (error-free) coding!