
When it comes to dealing with mistakes, the first thing we usually learn is how to use effort problems. But is this really enough because our code base is more complicated? I’m not sure Relying on a thorough effort can lead to repeated, random, and difficult retention code.
In this article, I will walk you 5 Advanced yet practical error samples It can make your code clean, more reliable and easier to debug. Each sample comes with the example of a real world so that you can clearly see where and why it means. So, let’s start.
1. Collecting error for batch processing
When taking action on multiple items (such as, in a loop), you want to continue processing even if some items fail, then finally report all the mistakes. This sample, called To collect the error.Avoid staying at the first failure. This sample form is best for verification, data import scenarios, or any situation where you want to provide comprehensive feedback on all matters rather than stop the first error.
Example: Action on the list of user records. Continue even if something fails.
def process_user_record(record, record_number):
if not record.get("email"):
raise ValueError(f"Record #{record_number} failed: Missing email in record {record}")
# Simulate processing
print(f"Processed user #{record_number}: {record('email')}")
def process_users(records):
errors = ()
for index, record in enumerate(records, start=1):
try:
process_user_record(record, index)
except ValueError as e:
errors.append(str(e))
return errors
users = (
{"email": "qasim@example.com"},
{"email": ""},
{"email": "zeenat@example.com"},
{"email": ""}
)
errors = process_users(users)
if errors:
print("\nProcessing completed with errors:")
for error in errors:
print(f"- {error}")
else:
print("All records processed successfully")
This code stands through user records and operates each individually. If an email is missing in a record, it increases the volir, which is caught and saved in the list of errors. This process continues for all records, and any failure is reported at the end without stopping the entire batch of such:
Output:
Processed user #1: qasim@example.com
Processed user #3: zeenat@example.com
Processing completed with errors:
- Record #2 failed: Missing email in record {'email': ''}
- Record #4 failed: Missing email in record {'email': ''}
2 of the context manager for resource management
When working with resources like files, database connections, or network sockets, you have to make sure they are properly opened and closed, even if an error occurs. Context managers, using the statement, automatically handle it, and reduce the possibility of resources leakage compared to manual trial blocks. This sample is especially helpful for I/O operations or to deal with external systems.
Example: We say you are reading the CSV file and want to make sure it is properly closed, even if the file is not prosecuted.
import csv
def read_csv_data(file_path):
try:
with open(file_path, 'r') as file:
print(f"Inside 'with': file.closed = {file.closed}") # Should be False
reader = csv.reader(file)
for row in reader:
if len(row) < 2:
raise ValueError("Invalid row format")
print(row)
print(f"After 'with': file.closed = {file.closed}") # Should be True
except FileNotFoundError:
print(f"Error: File {file_path} not found")
print(f"In except block: file is closed? {file.closed}")
except ValueError as e:
print(f"Error: {e}")
print(f"In except block: file is closed? {file.closed}")
# Create test file
with open("data.csv", "w", newline="") as f:
writer = csv.writer(f)
writer.writerows((("Name", "Age"), ("Sarwar", "30"), ("Babar"), ("Jamil", "25")))
# Run
read_csv_data("data.csv")
This code uses the file to open and read with a statement (context manager). If any rows have less than 2 values, it lifts one ValleyBut the file still closes. File.Closed Both inside and after the check file confirms both inside and both – even in case of an error. Let’s run the above code to observe this behavior:
Output:
Inside 'with': file.closed = False
('Name', 'Age')
('Sarwar', '30')
Error: Invalid row format
In except block: file is closed? True
3. Wrap the exception for the mistakes of the context
Sometimes, an exception in the lower -level function does not provide enough context on what goes wrong in a wider application. The exception wrapping (or chain) lets you get an exception, add context and re -raise a new discount that includes the original. It is especially useful in layered applications (eg, APIS or services).
Example: Suppose you are taking the user data from the database and want to provide contexts when the database error occurs.
class DatabaseAccessError(Exception):
"""Raised when database operations fail."""
pass
def fetch_user(user_id):
try:
# Simulate database query
raise ConnectionError("Failed to connect to database")
except ConnectionError as e:
raise DatabaseAccessError(f"Failed to fetch user {user_id}") from e
try:
fetch_user(123)
except DatabaseAccessError as e:
print(f"Error: {e}")
print(f"Caused by: {e.__cause__}")
The connection of the connection Caught and wrapped in a Database ECC ESRR With additional context about user identity. E -syrup connects the actual exception, so a complete error series is available for debugging. Output can look like this:
Output:
Error: Failed to fetch user 123
Caused by: Failed to connect to database
4. Re -try logic for temporary failures
Some errors, such as a network timeout or lack of temporary service, are temporary and can be resolved again. The use of a re -trying pattern can beautifully handle their code with manual loops without clutter. It automates recovery from temporary failures.
Example: Let’s try again on a Flucky API call that sometimes fails due to artificial network errors. The code below has tried the API call several times with a fixed delay in efforts. If the call is successful, it immediately results. If all efforts fail, it takes exceptions to handle the caller.
import random
import time
def flaky_api_call():
# Simulate 50% chance of failure (like timeout or server error)
if random.random() < 0.5:
raise ConnectionError("Simulated network failure")
return {"status": "success", "data": (1, 2, 3)}
def fetch_data_with_retry(retries=4, delay=2):
attempt = 0
while attempt < retries:
try:
result = flaky_api_call()
print("API call succeeded:", result)
return result
except ConnectionError as e:
attempt += 1
print(f"Attempt {attempt} failed: {e}. Retrying in {delay} seconds...")
time.sleep(delay)
raise ConnectionError(f"All {retries} attempts failed.")
try:
fetch_data_with_retry()
except ConnectionError as e:
print("Final failure:", e)
Output:
Attempt 1 failed: Simulated network failure. Retrying in 2 seconds...
API call succeeded: {'status': 'success', 'data': (1, 2, 3)}
As you can see, the first attempt failed due to artificial network error (which is 50 % time). The re -attempting logic waited for 2 seconds and then successfully completed the API call on the next attempt.
5. Customs exception classes for domain -related errors
Instead of relying on the general exception Valley Or Run TimeYou can create custom exception classes to represent specific errors in your application domain. This is more spiritual and easy to maintain.
Example: Suppose the payment processing system where different types of payment failures require specific handling.
class PaymentError(Exception):
"""Base class for payment-related exceptions."""
pass
class InsufficientFundsError(PaymentError):
"""Raised when the account has insufficient funds."""
pass
class InvalidCardError(PaymentError):
"""Raised when the card details are invalid."""
pass
def process_payment(amount, card_details):
try:
if amount > 1000:
raise InsufficientFundsError("Not enough funds for this transaction")
if not card_details.get("valid"):
raise InvalidCardError("Invalid card details provided")
print("Payment processed successfully")
except InsufficientFundsError as e:
print(f"Payment failed: {e}")
# Notify user to top up account
except InvalidCardError as e:
print(f"Payment failed: {e}")
# Prompt user to re-enter card details
except Exception as e:
print(f"Unexpected error: {e}")
# Log for debugging
process_payment(1500, {"valid": False})
Customs exceptions (inadequate funds, analodcards) will inherit from the base payment class, which you can usually handle specific payment issues, catching unexpected mistakes with discount blocks. For example, I Call Process_Pimnt (1500, {“Correct”: Invalid})First check triggers, because the amount (1500) is more than 1000, so it increases the incompetent fund. This exception is stuck in the same block, in addition to printing:
Output:
Payment failed: Not enough funds for this transaction
Conclusion
Just In this article, we discovered 5 practical error samples:
- Error Collect: Take action on all items, collect errors, and report them together
- Context Manager: Manage the resources like files with blocks
- Exception wrapping: Add the context by catching and collecting the exceptions
- Re -Logic Re -try: Automatically Repeat temporary errors like network failures
- Customs exception: Create specific error classes for clear handling
Try these patterns in your next project. With a little exercise, you will have the ease of maintaining your code and handling your error more efficiently.
Kanwal seals Kanwal is a machine learning engineer and a technical writer who has a deep passion for data science and has AI intersection with medicine. He authored EBook with “Maximum Production Capacity with Chat GPT”. As a Google Generation Scholar 2022 for the APAC, the Champions Diversity and Educational Virtue. He is also recognized as a tech scholar, Mitacs Global Research Scholar, and Harvard Vacod Scholar as a Taradata diversity. Kanwal is a passionate lawyer for change, who has laid the foundation of a Fame Code to empower women in stem fields.