Error Handling Debugging Practice Questions
Python Exception Handling & Debugging Practice Companion
This workbook contains 50 progressive, high-quality programming exercises categorized into 5 distinct levels of difficulty. Each question represents an operational, real-world data processing scenario solved using Python's robust exception handling mechanisms and debugging frameworks.
Every single solution is complete and executable, showcasing standard exception patterns (including try-except blocks, multi-exception catching, else/finally executions, assertions, custom exception classes, propagation, custom context managers, pdb instrumentation, and standard library logging). There are zero database engines (no SQLite) and zero external packages (no Pandas) to ensure absolute compliance with the Phase 1 Python Foundations syllabus.
Table of Contents
Level 1: Basic Exception Catching & Flow
Question 1: Catching ZeroDivisionError
Write a function safe_divide(numerator, denominator) that performs division, catches ZeroDivisionError gracefully, logs a warning message, and returns None instead of crashing.
main.pyRunnabledef safe_divide(numerator, denominator): try: return numerator / denominator except ZeroDivisionError: print("[Warning] Attempted division by zero.") return None ## Verification print(f"Valid: {safe_divide(10, 2)}") print(f"Invalid: {safe_divide(10, 0)}")
Expected Output:
Valid: 5.0 [Warning] Attempted division by zero. Invalid: None
Question 2: ValueError in Type Conversions
Write a function convert_to_integer(value) that attempts to convert a string input into an integer. Catch ValueError and return -1 if the string cannot be converted.
main.pyRunnabledef convert_to_integer(value): try: return int(value) except ValueError: print(f"[Warning] Conversion failed for value: '{value}'") return -1 ## Verification print(f"Success: {convert_to_integer('42')}") print(f"Failure: {convert_to_integer('abc')}")
Expected Output:
Success: 42 [Warning] Conversion failed for value: 'abc' Failure: -1
Question 3: Index Safety Verification
Write a function retrieve_element(data_list, index) that retrieves an element from a list at a specified index. Catch IndexError if the index is out of bounds and return None.
main.pyRunnabledef retrieve_element(data_list, index): try: return data_list[index] except IndexError: print(f"[Warning] Index {index} is out of bounds.") return None ## Verification scores = [90, 85, 88] print(f"Index 1: {retrieve_element(scores, 1)}") print(f"Index 5: {retrieve_element(scores, 5)}")
Expected Output:
Index 1: 85 [Warning] Index 5 is out of bounds. Index 5: None
Question 4: Dictionary Key Safety
Write a function get_student_grade(gradebook, student_name) that retrieves a student's grade from a dictionary. Catch KeyError if the student name does not exist and return "N/A".
main.pyRunnabledef get_student_grade(gradebook, student_name): try: return gradebook[student_name] except KeyError: print(f"[Warning] Key '{student_name}' not found.") return "N/A" ## Verification grades = {"Alice": "A", "Bob": "B"} print(f"Alice: {get_student_grade(grades, 'Alice')}") print(f"Charlie: {get_student_grade(grades, 'Charlie')}")
Expected Output:
Alice: A [Warning] Key 'Charlie' not found. Charlie: N/A
Question 5: Parameter Type Inspection
Write a function calculate_square(value) that validates that the parameter is an integer or float, catching TypeError and returning 0.0 if an invalid type is passed.
main.pyRunnabledef calculate_square(value): try: return value ** 2 except TypeError: print(f"[Error] Operation unsupported for type: {type(value)}") return 0.0 ## Verification print(f"Valid: {calculate_square(4.0)}") print(f"Invalid: {calculate_square('four')}")
Expected Output:
Valid: 16.0 [Error] Operation unsupported for type: <class 'str'> Invalid: 0.0
Question 6: Multiple Exception Blocks
Write a function parse_and_divide(string_num, divisor) that converts string_num to a float and divides it by divisor. Implement separate except blocks for ValueError and ZeroDivisionError.
main.pyRunnabledef parse_and_divide(string_num, divisor): try: parsed = float(string_num) return parsed / divisor except ValueError: print("[Error] Conversion ValueError occurred.") return None except ZeroDivisionError: print("[Error] Math ZeroDivisionError occurred.") return None ## Verification print(f"Normal: {parse_and_divide('100.0', 5)}") print(f"Bad Value: {parse_and_divide('invalid', 5)}") print(f"Bad Math: {parse_and_divide('100.0', 0)}")
Expected Output:
Normal: 20.0 [Error] Conversion ValueError occurred. Bad Value: None [Error] Math ZeroDivisionError occurred. Bad Math: None
Question 7: Accessing Exception Messages
Write a function print_exception_message(numerator, denominator) that performs division, catches a ZeroDivisionError, and prints the default error message generated by the Python interpreter using the as error syntax.
main.pyRunnabledef print_exception_message(numerator, denominator): try: return numerator / denominator except ZeroDivisionError as error: print(f"Captured standard message: '{error}'") return None ## Verification print_exception_message(10, 0)
Expected Output:
Captured standard message: 'division by zero'
Question 8: Implementing Else Execution
Write a function safe_file_open_demo(filename) that attempts to open a file. If successful, use the else block to read and print the file contents. Catch FileNotFoundError if the file does not exist.
main.pyRunnableimport pathlib def safe_file_open_demo(filename): try: file_stream = open(filename, "r", encoding="utf-8") except FileNotFoundError: print(f"Handling: '{filename}' does not exist.") else: # Executes only if open succeeded content = file_stream.read() print(f"File Contents: {content.strip()}") file_stream.close() ## Verification path = pathlib.Path("temp_test.txt") path.write_text("Instructional Data Payload", encoding="utf-8") safe_file_open_demo("temp_test.txt") safe_file_open_demo("missing_test_file.txt") ## Cleanup path.unlink()
Expected Output:
File Contents: Instructional Data Payload Handling: 'missing_test_file.txt' does not exist.
Question 9: Enforcing Resource Release
Write a function process_transaction_log(filename) that guarantees that an open file stream is closed using a finally block, regardless of whether a runtime exception occurs during reading.
main.pyRunnableimport pathlib def process_transaction_log(filename): file_stream = None try: file_stream = open(filename, "r", encoding="utf-8") # Trigger IndexError manually during reading data = file_stream.readlines() corrupted_access = data[99] # Out of bounds except FileNotFoundError: print("[Handling] File not found.") except IndexError: print("[Handling] IndexError caught during processing.") finally: # Guarantee resource release if file_stream: file_stream.close() print("[Finally] Verified file stream closure.") ## Verification path = pathlib.Path("log.txt") path.write_text("Transaction line 1\n", encoding="utf-8") process_transaction_log("log.txt") ## Cleanup path.unlink()
Expected Output:
[Handling] IndexError caught during processing. [Finally] Verified file stream closure.
Question 10: Conditional Type Checking
Write a function safe_length(item) that returns the length of item using len(). Instead of using try-except, prevent exceptions by checking if the object supports length checking using isinstance().
main.pyRunnabledef safe_length(item): # LBYL style - prevent exception beforehand if isinstance(item, (str, list, dict, set, tuple)): return len(item) print(f"Item of type {type(item)} does not support len().") return -1 ## Verification print(f"List length: {safe_length([1, 2, 3])}") print(f"Integer length: {safe_length(100)}")
Expected Output:
List length: 3 Item of type <class 'int'> does not support len(). Integer length: -1
Level 1 Checkpoint
Level 2: Constructors, Assertions, and Explicit Raising
Question 11: Raising ValueError in Initializers
Define a class RegisteredUserProfile that accepts username and age. Raise a ValueError in the constructor if the username is shorter than 4 characters, or if the age is negative.
main.pyRunnableclass RegisteredUserProfile: def __init__(self, username, age): if len(username) < 4: raise ValueError("Username must be at least 4 characters long.") if age < 0: raise ValueError("Age cannot be negative.") self.username = username self.age = age ## Verification try: user = RegisteredUserProfile("bob", 20) except ValueError as error: print(f"Registration Denied: {error}")
Expected Output:
Registration Denied: Username must be at least 4 characters long.
Question 12: Chaining Constructor Type-Checks
Extend RegisteredUserProfile by raising a TypeError in the constructor if username is not a string, or if age is not an integer.
main.pyRunnableclass RegisteredUserProfile: def __init__(self, username, age): if not isinstance(username, str): raise TypeError("Username must be a string.") if not isinstance(age, int): raise TypeError("Age must be an integer.") self.username = username self.age = age ## Verification try: user = RegisteredUserProfile("Alice", "twenty") except TypeError as error: print(f"Invalid Initialization: {error}")
Expected Output:
Invalid Initialization: Age must be an integer.
Question 13: Enforcing Invariants via Assert
Write a function adjust_classroom_thermostat(temp_celsius) that uses an assert statement to verify that the temperature remains within a safe range of 15 to 28 degrees.
main.pyRunnabledef adjust_classroom_thermostat(temp_celsius): # Enforce invariant bounds during development assert 15 <= temp_celsius <= 28, f"Temperature {temp_celsius}C exceeds safe limits." return f"Thermostat successfully set to {temp_celsius}C." ## Verification print(adjust_classroom_thermostat(20)) try: adjust_classroom_thermostat(35) except AssertionError as error: print(f"Assertion failed: {error}")
Expected Output:
Thermostat successfully set to 20C. Assertion failed: Temperature 35C exceeds safe limits.
Question 14: Asserting Post-Conditions
Write a function calculate_gpa_metrics(total_grade_points, total_courses) that uses assertions to check preconditions (valid inputs) and a post-condition (calculated GPA cannot exceed 4.0).
main.pyRunnabledef calculate_gpa_metrics(total_grade_points, total_courses): assert total_courses > 0, "Course count must be positive." gpa = total_grade_points / total_courses # Post-Condition Verification assert gpa <= 4.0, f"Calculated GPA {gpa:.2f} cannot exceed maximum scale value of 4.0." return gpa ## Verification print(f"GPA: {calculate_gpa_metrics(15.2, 4):.2f}") try: calculate_gpa_metrics(25.0, 5) # Yields a GPA of 5.0 except AssertionError as error: print(f"Verification Failure: {error}")
Expected Output:
GPA: 3.80 Verification Failure: Calculated GPA 5.00 cannot exceed maximum scale value of 4.0.
Question 15: Factory Parameter Verification
Define a class DatabaseConfig. Implement a @classmethod factory named from_dict(cls, data_dict) that validates that all required connection keys exist in the dictionary, raising a KeyError if any are missing.
main.pyRunnableclass DatabaseConfig: def __init__(self, host, port): self.host = host self.port = port @classmethod def from_dict(cls, data_dict): required = ["host", "port"] for key in required: if key not in data_dict: raise KeyError(f"Missing required database parameter: '{key}'") return cls(data_dict["host"], data_dict["port"]) ## Verification try: config = DatabaseConfig.from_dict({"host": "localhost"}) except KeyError as error: print(f"Configuration Rejected: {error}")
Expected Output:
Configuration Rejected: "Missing required database parameter: 'port'"
Question 16: Defensive Path Verification
Write a function process_data_file(filepath) that checks if a file path exists and is a file using pathlib before attempting to open it (LBYL style). If validation fails, raise a FileNotFoundError.
main.pyRunnableimport pathlib def process_data_file(filepath): path = pathlib.Path(filepath) # LBYL verification if not path.exists() or not path.is_file(): raise FileNotFoundError(f"Target data file not resolved at: {filepath}") with open(path, "r", encoding="utf-8") as f: return f.read() ## Verification try: process_data_file("missing_manifest.json") except FileNotFoundError as error: print(f"Failed path resolution: {error}")
Expected Output:
Failed path resolution: Target data file not resolved at: missing_manifest.json
Question 17: Re-Raising Exceptions
Write a function execute_transaction_step(ledger_action) that attempts to execute a function. Catch Exception, log a diagnostic warning message, and then re-raise the caught exception using a bare raise statement.
main.pyRunnabledef execute_transaction_step(ledger_action): try: ledger_action() except Exception as error: print("[Diagnostic Log] Transaction step failed. Re-raising exception for parent recovery...") raise # Re-raises the active exception def buggy_routine(): return 100 / 0 ## Verification try: execute_transaction_step(buggy_routine) except ZeroDivisionError: print("Caught re-raised ZeroDivisionError in parent scope.")
Expected Output:
[Diagnostic Log] Transaction step failed. Re-raising exception for parent recovery... Caught re-raised ZeroDivisionError in parent scope.
Question 18: Observing Exception Bubbling
Write three nested functions: level_three(), level_two(), and level_one(). Raise an exception in level_three() and show how it bubbles up the call stack to be caught in level_one().
main.pyRunnabledef level_three(): print("Entering level three...") raise ValueError("Error raised deep inside Level Three.") def level_two(): print("Entering level two...") level_three() def level_one(): print("Entering level one...") try: level_two() except ValueError as error: print(f"Successfully caught bubbled error in Level One: '{error}'") ## Verification level_one()
Expected Output:
Entering level one... Entering level two... Entering level three... Successfully caught bubbled error in Level One: 'Error raised deep inside Level Three.'
Question 19: Dynamic Context Logging
Define a class SystemErrorLogger that aggregates metadata from caught exceptions. Write a function that catches an exception, instantiates the logger, and stores the exception's name and message as attributes.
main.pyRunnableclass SystemErrorLogger: def __init__(self, exception): self.error_class = exception.__class__.__name__ self.message = str(exception) ## Verification try: result = [1, 2][99] except IndexError as e: logger = SystemErrorLogger(e) print(f"Logged Class: {logger.error_class} | Message: {logger.message}")
Expected Output:
Logged Class: IndexError | Message: list index out of range
Question 20: Copy Protections
Demonstrate how attempting to copy an uncopyable object (like an open file stream or a network socket) raises a TypeError when using Python's copy module. Catch the exception and handle it gracefully.
main.pyRunnableimport copy import pathlib ## Create temporary file path = pathlib.Path("temp.txt") path.write_text("Data", encoding="utf-8") file_stream = open("temp.txt", "r", encoding="utf-8") try: # Attempting to copy an active file stream raises a TypeError copied_stream = copy.deepcopy(file_stream) except TypeError as error: print(f"Deepcopy blocked: {error}") finally: file_stream.close() path.unlink()
Expected Output:
Deepcopy blocked: cannot pickle '_io.TextIOWrapper' object
Level 2 Checkpoint
Level 3: Custom Domain-Specific Exceptions
Question 21: Basic Custom Exception
Create a custom exception class named InvalidTransactionError that inherits from Exception. Write a function that raises this exception when an invalid transaction type is requested.
main.pyRunnableclass InvalidTransactionError(Exception): """Raised when a requested transaction type is invalid""" pass def process_transaction_type(tx_type): valid = ["deposit", "withdrawal"] if tx_type not in valid: raise InvalidTransactionError(f"Transaction type '{tx_type}' is unsupported.") return f"Processing {tx_type}" ## Verification try: process_transaction_type("wire_transfer") except InvalidTransactionError as error: print(f"Custom Error Caught: {error}")
Expected Output:
Custom Error Caught: Transaction type 'wire_transfer' is unsupported.
Question 22: Subclassing Lookup Errors
Create a custom exception class named StudentNotFoundError that inherits from KeyError. Write a student lookup function that raises this exception if a student ID is missing from the database.
main.pyRunnableclass StudentNotFoundError(KeyError): """Raised when a student record cannot be resolved by ID""" pass def query_student_record(database, student_id): if student_id not in database: # KeyErrors display the raw key, so pass a custom message string raise StudentNotFoundError(f"No student record resolves to ID: '{student_id}'") return database[student_id] ## Verification db = {"S101": "Alice Johnson"} try: query_student_record(db, "S999") except StudentNotFoundError as error: print(f"System Error: {error}")
Expected Output:
System Error: "No student record resolves to ID: 'S999'"
Question 23: Passing Dynamic Context to Custom Exceptions
Define a custom exception InsufficientCreditsError. Modify its constructor to accept student_name, credits_needed, and credits_available, and format a detailed error message automatically.
main.pyRunnableclass InsufficientCreditsError(Exception): def __init__(self, student_name, credits_needed, credits_available): self.student_name = student_name self.credits_needed = credits_needed self.credits_available = credits_available message = ( f"Student '{student_name}' failed graduation audit: " f"Needs {credits_needed} credits, but has only {credits_available}." ) super().__init__(message) ## Verification try: raise InsufficientCreditsError("Alice Johnson", 120, 115) except InsufficientCreditsError as error: print(error) print(f"Audit Context - Missing credits: {error.credits_needed - error.credits_available}")
Expected Output:
Student 'Alice Johnson' failed graduation audit: Needs 120 credits, but has only 115. Audit Context - Missing credits: 5
Question 24: Custom Exception Hierarchy
Design a two-level custom exception hierarchy for a school enrollment system: a base exception EnrollmentError, and two derived exceptions: CourseFullError and PrerequisiteMissingError.
main.pyRunnableclass EnrollmentError(Exception): """Base exception class for all enrollment failures""" pass class CourseFullError(EnrollmentError): """Raised when course capacity is exceeded""" pass class PrerequisiteMissingError(EnrollmentError): """Raised when prerequisite courses have not been completed""" pass def enroll_in_advanced_physics(has_intro_physics, current_seats, max_seats): if not has_intro_physics: raise PrerequisiteMissingError("Prerequisite course 'Intro to Physics' is missing.") if current_seats >= max_seats: raise CourseFullError("Course capacity has been reached.") return "Enrollment successful!" ## Verification try: enroll_in_advanced_physics(has_intro_physics=False, current_seats=20, max_seats=20) except EnrollmentError as error: # Polymorphically catches either subclass print(f"Enrollment Failure Caught: {error}")
Expected Output:
Enrollment Failure Caught: Prerequisite course 'Intro to Physics' is missing.
Question 25: Slots Enforced Exceptions
Define a custom exception SystemPanicError that uses __slots__ to restrict instance attributes strictly to error_code and timestamp.
main.pyRunnableclass SystemPanicError(Exception): __slots__ = ["error_code", "timestamp"] def __init__(self, error_code, timestamp): self.error_code = error_code self.timestamp = timestamp super().__init__(f"System panic [{error_code}] triggered at timestamp: {timestamp}") ## Verification panic = SystemPanicError("ERR-909", 1716124500) try: # Attempting to dynamically inject an attribute not defined in slots raises an AttributeError panic.severity = "high" except AttributeError as error: print(f"Dynamic exception modification blocked: {error}")
Expected Output:
Dynamic exception modification blocked: 'SystemPanicError' object has no attribute 'severity'
Question 26: Exception Translation Pattern
Write a function parse_school_roster_data(csv_row) that attempts to split and parse columns. Catch a standard index error (IndexError), and translate it into a custom MalformedRowError containing the raw row context.
main.pyRunnableclass MalformedRowError(Exception): """Raised when row data has missing fields or is malformed""" pass def parse_school_roster_data(csv_row): try: parts = csv_row.split(",") name = parts[0] grade = parts[1] gpa = parts[2] return {"name": name, "grade": int(grade), "gpa": float(gpa)} except IndexError as error: # Translate standard IndexError into a domain-specific custom exception raise MalformedRowError(f"Row data is missing required fields: '{csv_row}'") from error ## Verification try: parse_school_roster_data("Alice,11") # Missing GPA column except MalformedRowError as error: print(f"Data Pipeline Alert: {error}")
Expected Output:
Data Pipeline Alert: Row data is missing required fields: 'Alice,11'
Question 27: Custom Exception String Formatting
Override the magic __str__ method on a custom exception class NetworkConnectionTimeoutError to return a detailed, formatted error message automatically when str() is called on the exception object.
main.pyRunnableclass NetworkConnectionTimeoutError(Exception): def __init__(self, host, port, timeout_sec): self.host = host self.port = port self.timeout_sec = timeout_sec def __str__(self): return f"NetworkTimeout: Connection to host '{self.host}:{self.port}' timed out after {self.timeout_sec} seconds." ## Verification error = NetworkConnectionTimeoutError("api.school.edu", 443, 10) print(str(error))
Expected Output:
NetworkTimeout: Connection to host 'api.school.edu:443' timed out after 10 seconds.
Question 28: Safe Configuration Translation
Define a class ConfigTranslator. Write a method load_config(file_path) that catches OSError or json.JSONDecodeError during reading, and raises a custom ConfigurationInitializationError instead.
main.pyRunnableimport json import pathlib class ConfigurationInitializationError(Exception): """Raised when system configurations fail to initialize from disk""" pass class ConfigTranslator: def load_config(self, file_path): try: with open(file_path, "r", encoding="utf-8") as f: return json.load(f) except (OSError, json.JSONDecodeError) as error: raise ConfigurationInitializationError(f"Initialization abort: Failed parsing config file. Error: {error}") ## Verification path = pathlib.Path("bad_config.json") path.write_text("{invalid_json_payload}", encoding="utf-8") translator = ConfigTranslator() try: translator.load_config("bad_config.json") except ConfigurationInitializationError as error: print(error) ## Cleanup path.unlink()
Expected Output:
Initialization abort: Failed parsing config file. Error: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
Question 29: Checking Validation States
Define a class FormValidator. Implement a private method _verify_email(email) that returns a boolean validation state, and a public method register(email) that raises a custom ValidationError if the email domain is invalid.
main.pyRunnableclass ValidationError(Exception): """Raised when form validations fail""" pass class FormValidator: def _verify_email(self, email): """Private verification helper returning boolean""" return isinstance(email, str) and email.endswith("@school.edu") def register(self, email): """Public interface raising exception on validation failure""" if not self._verify_email(email): raise ValidationError(f"Email '{email}' does not belong to a valid school domain.") return "Registration successful!" ## Verification validator = FormValidator() try: validator.register("bob@gmail.com") except ValidationError as error: print(error)
Expected Output:
Email 'bob@gmail.com' does not belong to a valid school domain.
Question 30: Grouped Exceptions Pipeline
Define a class BulkValidationPipeline. Implement a method validate_roster(rosters_list) that processes a list of names, collects all names that fail validation (fewer than 3 characters), and raises a single ValidationError containing the list of all invalid names.
main.pyRunnableclass BulkValidationError(Exception): def __init__(self, invalid_records_list): self.invalid_records = invalid_records_list super().__init__(f"Bulk validation failed for records: {invalid_records_list}") class BulkValidationPipeline: def validate_roster(self, rosters_list): invalid_names = [] for name in rosters_list: if not isinstance(name, str) or len(name) < 3: invalid_names.append(name) if invalid_names: raise BulkValidationError(invalid_names) return "All records passed validation!" ## Verification pipeline = BulkValidationPipeline() try: pipeline.validate_roster(["Alice", "Jo", "Bob", "Ed"]) except BulkValidationError as error: print(error) print(f"List of invalid names: {error.invalid_records}")
Expected Output:
Bulk validation failed for records: ['Jo', 'Ed'] List of invalid names: ['Jo', 'Ed']
Level 3 Checkpoint
Level 4: Advanced Control Flow & Context Managers
Question 31: Basic Safe File Context Manager
Write a function safe_write_content(filepath, data) that implements safe file opening using Python's standard with context manager, guaranteeing file closure even if writing fails.
main.pyRunnabledef safe_write_content(filepath, data): # Enforces EAFP and automatic file resource cleanup try: with open(filepath, "w", encoding="utf-8") as f: f.write(data) print("[System] Write operations completed successfully.") except OSError as error: print(f"[OSError Caught] Failed writing to path: {error}") ## Verification safe_write_content("nonexistent_folder/file.txt", "payload")
Expected Output:
[OSError Caught] Failed writing to path: [Errno 2] No such file or directory: 'nonexistent_folder/file.txt'
Question 32: Custom Context Manager Class
Define a class NetworkResourceConnection that implements __enter__ and __exit__. The class must simulate initializing a network connection on enter and closing it on exit.
main.pyRunnableclass NetworkResourceConnection: def __init__(self, host): self.host = host def __enter__(self): print(f"[Resource] Opening persistent connection to host: '{self.host}'") return self def __exit__(self, exc_type, exc_val, exc_tb): print(f"[Resource] Closing persistent connection to host: '{self.host}'") # Returns None (or False) to let exceptions propagate if they occur return False ## Verification with NetworkResourceConnection("api.school.edu") as connection: print("Executing network operations inside context.")
Expected Output:
[Resource] Opening persistent connection to host: 'api.school.edu' Executing network operations inside context. [Resource] Closing persistent connection to host: 'api.school.edu'
Question 33: Suppressing Exceptions in Context Managers
Modify NetworkResourceConnection so that its __exit__ method checks if the exception type is a ValueError. If so, suppress the exception by returning True, preventing it from bubbling up and crashing the program.
main.pyRunnableclass SuppressedConnection(NetworkResourceConnection): def __exit__(self, exc_type, exc_val, exc_tb): print(f"[Resource] Closing connection to host: '{self.host}'") if exc_type is ValueError: print(f"[Resource] Suppressing caught ValueError: '{exc_val}'") return True # Suppress the exception return False # Let other exception types bubble up ## Verification with SuppressedConnection("api.school.edu"): print("Attempting operation that raises a ValueError...") raise ValueError("Invalid network protocol parameter.") print("Context exited safely. Execution continues without crashing!")
Expected Output:
[Resource] Opening persistent connection to host: 'api.school.edu' Attempting operation that raises a ValueError... [Resource] Closing connection to host: 'api.school.edu' [Resource] Suppressing caught ValueError: 'Invalid network protocol parameter.' Context exited safely. Execution continues without crashing!
Question 34: Logging Exceptions in Context Managers
Define a class ExceptionTelemetryTracker that acts as a context manager. Its __exit__ method must log the exception details to the console using logging.error if an error occurs, but allow the exception to bubble up by returning False.
main.pyRunnableimport logging class ExceptionTelemetryTracker: def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): if exc_type: logging.error(f"[Telemetry] Exception occurred: {exc_type.__name__} -> {exc_val}") return False # Let the exception bubble up to the parent caller ## Verification try: with ExceptionTelemetryTracker(): raise KeyError("Missing primary lookup key.") except KeyError: print("Exception successfully bubbled up and caught by parent try-except block.")
Expected Output:
2026-05-19 15:20:45 [ERROR] [Telemetry] Exception occurred: KeyError -> 'Missing primary lookup key.' Exception successfully bubbled up and caught by parent try-except block.
Question 35: Nested Context Managers
Open two files simultaneously using nested context managers. Safely copy the contents of the source file to the destination file, ensuring both file streams are closed automatically when done.
main.pyRunnableimport pathlib ## Setup files src_path = pathlib.Path("src.txt") dest_path = pathlib.Path("dest.txt") src_path.write_text("Instructional Data Payload", encoding="utf-8") ## Open both files safely using nested context managers with open(src_path, "r", encoding="utf-8") as src_file, open(dest_path, "w", encoding="utf-8") as dest_file: content = src_file.read() dest_file.write(content) print(f"Copied Content: {dest_path.read_text(encoding='utf-8')}") ## Cleanup src_path.unlink() dest_path.unlink()
Expected Output:
Copied Content: Instructional Data Payload
Question 36: Fallback Recovery Engine
Write a function query_user_gpa(primary_dict, backup_dict, student_id) that attempts to retrieve a GPA from a primary dictionary. If it raises a KeyError, fall back to a backup dictionary. If it is missing from both, return 0.0.
main.pyRunnabledef query_user_gpa(primary_dict, backup_dict, student_id): try: # Attempt primary lookup return primary_dict[student_id] except KeyError: print(f"[Warning] Student '{student_id}' missing in primary source. Falling back to backup...") try: # Attempt fallback lookup return backup_dict[student_id] except KeyError: print(f"[Error] Student '{student_id}' not found in either source.") return 0.0 ## Verification primary = {"S101": 3.8} backup = {"S101": 3.8, "S102": 3.5} print(f"S101 GPA: {query_user_gpa(primary, backup, 'S101')}") print(f"S102 GPA: {query_user_gpa(primary, backup, 'S102')}") print(f"S999 GPA: {query_user_gpa(primary, backup, 'S999')}")
Expected Output:
S101 GPA: 3.8 [Warning] Student 'S102' missing in primary source. Falling back to backup... S102 GPA: 3.5 [Warning] Student 'S999' missing in primary source. Falling back to backup... [Error] Student 'S999' not found in either source. S999 GPA: 0.0
Question 37: Abstract Validation Interfaces
Define an Abstract Base Class (ABC) named AbstractValidator containing an @abstractmethod named validate(data). Create a subclass GPAValidator that implements it and raises a ValueError if the GPA is out of bounds.
main.pyRunnablefrom abc import ABC, abstractmethod class AbstractValidator(ABC): @abstractmethod def validate(self, data): pass class GPAValidator(AbstractValidator): def validate(self, data): if not isinstance(data, (int, float)) or not (0.0 <= data <= 4.0): raise ValueError(f"GPA {data} must be a numeric value between 0.0 and 4.0.") return True ## Verification validator = GPAValidator() try: validator.validate(4.5) except ValueError as error: print(f"Validation failed: {error}")
Expected Output:
Validation failed: GPA 4.5 must be a numeric value between 0.0 and 4.0.
Question 38: Polymorphic Exceptions Processing
Write a function process_evaluations(validators_list, data) that runs multiple validation classes on the same dataset. Handle exceptions polymorphically, logging which validator failed.
main.pyRunnableclass EmailValidator(AbstractValidator): def validate(self, data): if "@" not in data: raise ValueError("Email is missing '@' symbol.") class DomainValidator(AbstractValidator): def validate(self, data): if not data.endswith(".edu"): raise ValueError("Domain must end in '.edu'.") def process_evaluations(validators_list, data): for val in validators_list: try: val.validate(data) except ValueError as error: # Polymorphic exception catching print(f"[Alert] Validator '{val.__class__.__name__}' rejected input. Reason: {error}") return False print("All validation checks passed!") return True ## Verification validators = [EmailValidator(), DomainValidator()] process_evaluations(validators, "alice_at_school.edu") process_evaluations(validators, "alice@gmail.com")
Expected Output: // Note how each validator's specific error message is caught and handled polymorphically:
[Alert] Validator 'EmailValidator' rejected input. Reason: Email is missing '@' symbol. [Alert] Validator 'DomainValidator' rejected input. Reason: Domain must end in '.edu'.
Question 39: Aggregating Multi-Class Validation Errors
Define a class ValidationRoster. Implement a method run_validations(validators_list, data) that runs a list of validators on a dataset, aggregates all raised validation exceptions into a list, and returns the list of errors.
main.pyRunnableclass ValidationRoster: def run_validations(self, validators_list, data): errors = [] for val in validators_list: try: val.validate(data) except ValueError as error: errors.append(error) return errors ## Verification validators = [GPAValidator(), EmailValidator()] roster = ValidationRoster() ## Pass 5.0 (invalid GPA) and "bad_email" (invalid email) failures = roster.run_validations(validators, "bad_email") for f in failures: print(f"Captured Error: {f}")
Expected Output:
Captured Error: GPA bad_email must be a numeric value between 0.0 and 4.0. Captured Error: Email is missing '@' symbol.
Question 40: Duck Typing Exception Handling
Write a function trigger_error_recovery(system_object) that invokes a .recover() method on any class passed to it (duck typing). Handle the exception if the class does not provide a .recover() method.
main.pyRunnableclass NetworkBackup: def recover(self): return "Restored connection via backup API gateway." class StorageBackup: def recover(self): return "Restored database from local cache snapshot." class SimpleObject: pass def trigger_error_recovery(system_object): try: # Duck typing: assume the object has a recover method print(system_object.recover()) except AttributeError: print(f"[Error Recovery System] Class '{system_object.__class__.__name__}' does not support recovery interfaces.") ## Verification trigger_error_recovery(NetworkBackup()) trigger_error_recovery(StorageBackup()) trigger_error_recovery(SimpleObject())
Expected Output:
Restored connection via backup API gateway. Restored database from local cache snapshot. [Error Recovery System] Class 'SimpleObject' does not support recovery interfaces.
Level 4 Checkpoint
Level 5: Advanced Debugging, Logging & Custom Pipelines
Question 41: Arithmetic Validation Checks
Define a class ClassroomCredits. Overload the addition + operator using __add__. Raise a TypeError if a user attempts to add an object that is not an instance of ClassroomCredits.
main.pyRunnableclass ClassroomCredits: def __init__(self, value): self.value = float(value) def __add__(self, other): if not isinstance(other, ClassroomCredits): raise TypeError(f"Incompatible addition: Cannot add ClassroomCredits to {type(other).__name__}.") return ClassroomCredits(self.value + other.value) ## Verification c1 = ClassroomCredits(12.5) try: result = c1 + 10 # Adding a raw integer is unsupported except TypeError as error: print(error)
Expected Output:
Incompatible addition: Cannot add ClassroomCredits to int.
Question 42: Development Diagnostic Formats (repr)
Define a class SystemErrorRecord that overrides __repr__ to return a detailed, developer-focused description of the exception, including its memory address.
main.pyRunnableclass SystemErrorRecord: def __init__(self, exception, module): self.exception = exception self.module = module def __repr__(self): return f"<SystemErrorRecord exception={self.exception.__class__.__name__} message='{self.exception}' module='{self.module}' at {hex(id(self))}>" ## Verification try: 1 / 0 except ZeroDivisionError as e: record = SystemErrorRecord(e, "FinanceModule") print(repr(record))
Expected Output:
<SystemErrorRecord exception=ZeroDivisionError message='division by zero' module='FinanceModule' at 0x7f81b8>
Question 43: Advanced Logger Class Wrapper
Define a class AdvancedLoggerWrapper that wraps the standard library's logging module, automatically appending system context data like memory ID and current line number to all log entries.
main.pyRunnableimport logging import inspect class AdvancedLoggerWrapper: def __init__(self, name): self.logger = logging.getLogger(name) def log_error(self, message): # Inspect the call stack dynamically to get the caller's frame details caller_frame = inspect.stack()[1] line = caller_frame.lineno filename = caller_frame.filename formatted = f"[{filename}:{line}] {message}" self.logger.error(formatted) ## Verification logging.basicConfig(level=logging.ERROR, format='%(asctime)s [%(levelname)s] %(message)s') app_logger = AdvancedLoggerWrapper("AppLogger") def test_function(): app_logger.log_error("Simulated database timeout error.") test_function()
Expected Output:
2026-05-19 15:25:30 [ERROR] [comprehensive_oop_database.py:16] Simulated database timeout error.
Question 44: Intercepting Attributes via getattr
Define a class SafeDynamicConfig that intercepts requests for missing attributes using __getattr__. The class must log a warning and raise a domain-specific AttributeError instead of returning None.
main.pyRunnableclass SafeDynamicConfig: def __init__(self): self.host = "127.0.0.1" def __getattr__(self, name): print(f"[Config Warning] Attempted access to undefined attribute: '{name}'") raise AttributeError(f"Configuration key '{name}' is not defined.") ## Verification config = SafeDynamicConfig() try: print(config.port) except AttributeError as error: print(f"Access Blocked: {error}")
Expected Output:
[Config Warning] Attempted access to undefined attribute: 'port' Access Blocked: Configuration key 'port' is not defined.
Question 45: Operational Retry Mechanics
Write a function retry_on_exception(attempts, exception_class, target_function, *args) that attempts to execute a function. If it raises an exception matching exception_class, retry the operation up to attempts times before raising the final exception.
main.pyRunnabledef retry_on_exception(attempts, exception_class, target_function, *args): current_attempt = 1 while current_attempt <= attempts: try: return target_function(*args) except exception_class as error: print(f"[Retry Engine] Attempt {current_attempt}/{attempts} failed with: {error}") if current_attempt == attempts: print("[Retry Engine] Maximum retry attempts reached. Raising final exception...") raise current_attempt += 1 def unstable_api_call(): raise OSError("Network connection lost.") ## Verification try: retry_on_exception(3, OSError, unstable_api_call) except OSError: print("Caught final OSError in parent scope.")
Expected Output:
[Retry Engine] Attempt 1/3 failed with: Network connection lost. [Retry Engine] Attempt 2/3 failed with: Network connection lost. [Retry Engine] Attempt 3/3 failed with: Network connection lost. [Retry Engine] Maximum retry attempts reached. Raising final exception... Caught final OSError in parent scope.
Question 46: Error Logging Decorator
Write a decorator log_exceptions_to_console that wraps a function, automatically intercepts any exceptions raised during execution, logs them to the console, and then re-raises them.
main.pyRunnabledef log_exceptions_to_console(func): def wrapper(*args, **kwargs): try: return func(*args, **kwargs) except Exception as error: print(f"[Decorator Exception Log] Function '{func.__name__}' failed. Error details: '{error}'") raise return wrapper @log_exceptions_to_console def calculate_division(a, b): return a / b ## Verification try: calculate_division(10, 0) except ZeroDivisionError: print("ZeroDivisionError successfully caught in parent scope.")
Expected Output:
[Decorator Exception Log] Function 'calculate_division' failed. Error details: 'division by zero' ZeroDivisionError successfully caught in parent scope.
Question 47: Traceback Extraction
Write a function extract_and_format_traceback(buggy_function) that catches an exception during execution and prints a detailed traceback to the console using the built-in traceback module.
main.pyRunnableimport traceback def extract_and_format_traceback(buggy_function): try: buggy_function() except Exception: print("Exception caught. Extracting traceback...") formatted_tb = traceback.format_exc() print("\n--- BEGIN FORMATTED TRACEBACK ---") # Expose call stack details print(formatted_tb.strip()) print("--- END FORMATTED TRACEBACK ---\n") def nested_crash(): return [1, 2][99] ## Verification extract_and_format_traceback(nested_crash)
Expected Output:
Exception caught. Extracting traceback... --- BEGIN FORMATTED TRACEBACK --- Traceback (most recent call last): File "comprehensive_oop_database.py", line 6, in extract_and_format_traceback buggy_function() File "comprehensive_oop_database.py", line 15, in nested_crash return [1, 2][99] IndexError: list index out of range --- END FORMATTED TRACEBACK ---
Question 48: Dict-Based Exception Mapper
Write a function resolve_exception_recovery(exception_object) that maps exception classes to specific recovery callback functions using a dictionary as an exception router.
main.pyRunnabledef recover_from_math_fault(): return "Action: Math Fault - Fallback balance initialized to 0.0" def recover_from_missing_file(): return "Action: File Fault - Reconstructed missing file from default catalog." def resolve_exception_recovery(exception_object): # Map exception classes to specific recovery callback functions exception_router = { ZeroDivisionError: recover_from_math_fault, FileNotFoundError: recover_from_missing_file } exc_class = exception_object.__class__ callback = exception_router.get(exc_class) if callback: return callback() return "Action: Default - General system recovery." ## Verification print(resolve_exception_recovery(ZeroDivisionError("division by zero"))) print(resolve_exception_recovery(FileNotFoundError("Missing file"))) print(resolve_exception_recovery(ValueError("General value error")))
Expected Output:
Action: Math Fault - Fallback balance initialized to 0.0 Action: File Fault - Reconstructed missing file from default catalog. Action: Default - General system recovery.
Question 49: Iterator Exception Stops
Define a class UnboundedRange that implements __iter__ and __next__. The iterator must raise a StopIteration exception when the returned value exceeds a specified limit.
main.pyRunnableclass UnboundedRange: def __init__(self, start, limit): self.current = start self.limit = limit def __iter__(self): return self def __next__(self): if self.current > self.limit: # Standard Python exception pattern to signal iterator exhaustion raise StopIteration val = self.current self.current += 1 return val ## Verification custom_range = UnboundedRange(1, 3) print(list(custom_range))
Expected Output:
[1, 2, 3]
Question 50: Unified High-Reliability Catalog Enrollment System
Implement a complete, production-grade Enrollment Management System. Combine:
- Custom exception classes:
CourseRegistryError,EnrollmentLimitExceeded, andValidationFailed. - Encapsulated class structures using
@propertygetters and setters to validate all inputs. - Write a
SessionContextcontext manager that supports atomic student enrollment batches, automatically rolling back the class roster to its original state if any enrollment fails. - Implement diagnostic logging throughout the system.
main.pyRunnable## comprehensive_catalog_system.py import json import logging import pathlib logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s] %(message)s') ## ===================================================================== ## 1. EXCEPTION HIERARCHY ## ===================================================================== class CourseRegistryError(Exception): """Base exception for all course registry faults""" pass class EnrollmentLimitExceeded(CourseRegistryError): """Raised when a course is full""" pass class ValidationFailed(CourseRegistryError): """Raised when validation checks on parameters fail""" pass ## ===================================================================== ## 2. COURSE BLUEPRINT ## ===================================================================== class CourseEnrollmentRegistry: def __init__(self, course_name, max_capacity): self.course_name = course_name self.max_capacity = max_capacity self.__roster = [] @property def max_capacity(self): return self.__max_capacity @max_capacity.setter def max_capacity(self, value): if not isinstance(value, int) or value <= 0: raise ValidationFailed("Maximum capacity must be a positive integer.") self.__max_capacity = value def get_roster(self): return list(self.__roster) def enroll(self, student_name): if not isinstance(student_name, str) or len(student_name.strip()) < 3: raise ValidationFailed(f"Student name '{student_name}' must be at least 3 characters long.") if len(self.__roster) >= self.__max_capacity: raise EnrollmentLimitExceeded(f"Course '{self.course_name}' is full. Limit: {self.__max_capacity}") self.__roster.append(student_name.strip()) logging.info(f"Student '{student_name}' successfully enrolled in {self.course_name}.") ## ===================================================================== ## 3. ATOMIC SESSION CONTEXT MANAGER ## ===================================================================== class EnrollmentSession: """Guarantees atomic student enrollment batches, rolling back all changes if any fail""" def __init__(self, registry): self.registry = registry self.backup_roster = [] def __enter__(self): # Create a deep copy backup of the roster before beginning the batch self.backup_roster = self.registry.get_roster() logging.info(f"[Session] Roster backup state created with {len(self.backup_roster)} students.") return self def __exit__(self, exc_type, exc_val, exc_tb): if exc_type: # Roll back registry roster to original backup state logging.error(f"[Session] Enrollment batch failed: {exc_val}") logging.info("[Session] Rolling back changes to backup state...") self.registry._CourseEnrollmentRegistry__roster = self.backup_roster # Suppress registry exceptions to prevent program crashes if issubclass(exc_type, CourseRegistryError): logging.info("[Session] Registry exception handled and suppressed safely.") return True else: logging.info("[Session] Enrollment batch completed successfully.") return False ## ===================================================================== ## 4. SYSTEM RUNTIME VERIFICATION ## ===================================================================== if __name__ == "__main__": # Initialize course registry course = CourseEnrollmentRegistry("AP Chemistry", max_capacity=3) # 1. Run a successful enrollment batch print("\n--- Batch 1: Successful Enrollment ---") with EnrollmentSession(course): course.enroll("Alice") course.enroll("Bob") print(f"Roster size: {len(course.get_roster())} | Students: {course.get_roster()}") # 2. Run a batch that fails validation (student name too short) print("\n--- Batch 2: Validation Failure (Rollback Expected) ---") with EnrollmentSession(course): course.enroll("Charlie") course.enroll("Ed") # Invalid name: triggers ValidationFailed and rollback print(f"Roster size: {len(course.get_roster())} | Students: {course.get_roster()}") # 3. Run a batch that exceeds course capacity print("\n--- Batch 3: Capacity Failure (Rollback Expected) ---") with EnrollmentSession(course): course.enroll("Charlie") course.enroll("David") # Course full: triggers EnrollmentLimitExceeded and rollback print(f"Roster size: {len(course.get_roster())} | Students: {course.get_roster()}")
Expected Output:
--- Batch 1: Successful Enrollment --- [Session] Roster backup state created with 0 students. Student 'Alice' successfully enrolled in AP Chemistry. Student 'Bob' successfully enrolled in AP Chemistry. [Session] Enrollment batch completed successfully. Roster size: 2 | Students: ['Alice', 'Bob'] --- Batch 2: Validation Failure (Rollback Expected) --- [Session] Roster backup state created with 2 students. Student 'Charlie' successfully enrolled in AP Chemistry. [Session] Enrollment batch failed: Student name 'Ed' must be at least 3 characters long. [Session] Rolling back changes to backup state... [Session] Registry exception handled and suppressed safely. Roster size: 2 | Students: ['Alice', 'Bob'] --- Batch 3: Capacity Failure (Rollback Expected) --- [Session] Roster backup state created with 2 students. Student 'Charlie' successfully enrolled in AP Chemistry. [Session] Enrollment batch failed: Course 'AP Chemistry' is full. Limit: 3 [Session] Rolling back changes to backup state... [Session] Registry exception handled and suppressed safely. Roster size: 2 | Students: ['Alice', 'Bob']
Level 5 Checkpoint
Finished this lesson?
Mark it complete to advance your curriculum progress.