Previous | Tutorial index | Next

πŸ“š Tutorial 2: Common Built-in Exception Types

Learning Objectives

1. Introduction: The Python Exception Landscape

Python provides a rich set of built-in exceptions that cover a wide range of error conditions. Understanding these exceptions is crucial for writing robust code and for effective debugging. When an exception occurs, Python raises an object of a specific exception class, and knowing which class it belongs to gives you immediate insight into what went wrong.

In this tutorial, we will explore eight of the most common built-in exceptions. For each exception, we will:

2. Key Terms

Before diving into the content, familiarise yourself with these key terms:

Term Definition
NameError Raised when a local or global name is not found.
TypeError Raised when an operation or function is applied to an object of inappropriate type.
ValueError Raised when a function receives an argument of the correct type but inappropriate value.
ZeroDivisionError Raised when the second argument of a division or modulo operation is zero.
FileNotFoundError Raised when a file or directory is requested but does not exist.
OSError Raised when a system-related operation causes an error.
RuntimeError A general-purpose exception for errors that don't fit other categories.
AssertionError Raised when an assert statement fails.

3. Exception Categories Overview

Before diving into individual exceptions, let's understand how they relate to each other:

BaseException └── Exception β”œβ”€β”€ NameError β”œβ”€β”€ TypeError β”œβ”€β”€ ValueError β”œβ”€β”€ ArithmeticError β”‚ └── ZeroDivisionError β”œβ”€β”€ LookupError β”‚ β”œβ”€β”€ IndexError β”‚ └── KeyError β”œβ”€β”€ OSError β”‚ └── FileNotFoundError β”œβ”€β”€ RuntimeError └── AssertionError

Key Insight: All the exceptions we'll study in this tutorial inherit from Exception (which itself inherits from BaseException). This means you can catch any of them using except Exception: if you want a general handler, but it's always better to catch specific exceptions first.

4. NameError: When Python Cannot Find a Name

4.1 What Causes a NameError?

A NameError is raised when Python encounters a name (a variable, function, class, or module name) that it cannot find in the current scope. This typically happens when:

4.2 Code Examples

Example 1: Using an undefined variable

print(my_variable) # NameError: name 'my_variable' is not defined

Example 2: Misspelling a function name

prnt("Hello") # NameError: name 'prnt' is not defined. Did you mean 'print'?

Example 3: Variable out of scope

def my_function(): inside_var = "I'm inside" my_function() print(inside_var) # NameError: name 'inside_var' is not defined

Example 4: Forgetting to import a module

sqrt(16) # NameError: name 'sqrt' is not defined # Correct way: import math print(math.sqrt(16)) # Works fine

4.3 Common Scenarios

Scenario Example
Typo in variable name user_naem instead of user_name
Using variable before assignment count += 1 when count wasn't initialized
Forgetting to define a function Calling calculate() before defining it
Misspelling a built-in function lenght() instead of len()

4.4 How to Handle a NameError

try: print(undefined_variable) except NameError: print("Error: A variable is being used before it was defined.")

4.5 Preventing NameErrors

  1. Initialize variables before use:

    count = 0 # Initialize count += 1 # Now safe
  2. Use descriptive variable names to avoid typos:

    user_name = "Alice" # Clear and less prone to typos
  3. Use an IDE with autocompletion to catch typos early.

  4. Check variable scope: Understand the difference between local and global scope.

5. TypeError: When Operations Have Incompatible Types

5.1 What Causes a TypeError?

A TypeError is raised when an operation or function is applied to an object of an inappropriate type. This is Python's way of saying, "I don't know how to do this with what you've given me."

5.2 Code Examples

Example 1: Adding incompatible types

result = "Hello" + 5 # TypeError: can only concatenate str (not "int") to str

Example 2: Calling a non-callable object

my_number = 42 my_number() # TypeError: 'int' object is not callable

Example 3: Using an incorrect number of arguments

def greet(name): print(f"Hello, {name}") greet("Alice", "Bob") # TypeError: greet() takes 1 positional argument but 2 were given

Example 4: Iterating over a non-iterable

for item in 42: # TypeError: 'int' object is not iterable print(item)

Example 5: Using a non-subscriptable type

my_number = 42 print(my_number[0]) # TypeError: 'int' object is not subscriptable

5.3 Common Scenarios

Operation Incompatible Types Error Message
Concatenation str + int can only concatenate str (not "int") to str
Multiplication str * str can't multiply sequence by non-int of type 'str'
Function Call Calling an integer 'int' object is not callable
Subscription Indexing an integer 'int' object is not subscriptable
Iteration Iterating over an integer 'int' object is not iterable

5.4 How to Handle a TypeError

try: result = "Hello" + 5 except TypeError: print("Error: Cannot add a string and an integer.") result = "Hello" + str(5) # Convert to string print(f"Fixed result: {result}")

5.5 Preventing TypeErrors

  1. Check types using type() or isinstance():

    if isinstance(value, int): print(value + 10) else: print("Expected an integer")
  2. Convert types explicitly:

    number_str = "42" number_int = int(number_str) # Convert before use result = number_int + 10
  3. Use Python's duck typing carefully: Write functions that work with any type that supports the required operations.

6. ValueError: When the Value is Right Type, Wrong Content

6.1 What Causes a ValueError?

A ValueError is raised when an operation or function receives an argument with the right type but an inappropriate value. The type is correct, but the specific value is not acceptable.

6.2 Code Examples

Example 1: Converting invalid string to integer

number = int("abc") # ValueError: invalid literal for int() with base 10: 'abc'

Example 2: Finding a substring that doesn't exist

text = "Hello World" position = text.index("xyz") # ValueError: substring not found

Example 3: Using an invalid mathematical operation

import math result = math.sqrt(-1) # ValueError: math domain error

Example 4: Removing an element that doesn't exist

my_list = [1, 2, 3] my_list.remove(5) # ValueError: list.remove(x): x not in list

6.3 Common Scenarios

Scenario Example
Invalid numeric conversion int("12.5") (a float string without decimal points)
Out-of-range in math functions math.log(0) or math.sqrt(-1)
Invalid date/time parsing datetime.strptime("2024-13-01", "%Y-%m-%d")
Empty sequence operations max([]) (empty iterable)

6.4 How to Handle a ValueError

try: user_age = int(input("Enter your age: ")) except ValueError: print("Error: Please enter a valid integer.") # You could retry, use a default value, or exit gracefully

6.5 Preventing ValueErrors

  1. Validate input before conversion:

    user_input = input("Enter a number: ") if user_input.isdigit(): number = int(user_input) else: print("Invalid input")
  2. Use try...except for conversions:

    try: number = int(user_input) except ValueError: number = 0 # Default value
  3. Check for conditions before using functions:

    value = -1 if value >= 0: result = math.sqrt(value) else: print("Cannot compute square root of negative number")

6.6 Important Distinction: ValueError vs TypeError

Aspect ValueError TypeError
What's wrong The value is incorrect The type is incorrect
Example int("abc") "abc" + 5
Type is Correct (string) Incorrect (string + int)
Value is Incorrect (not numeric) N/A

7. ZeroDivisionError: When You Divide by Zero

7.1 What Causes a ZeroDivisionError?

A ZeroDivisionError is raised when you attempt to divide a number by zero in a division or modulo operation. This is a specific type of ArithmeticError.

7.2 Code Examples

Example 1: Division by zero

result = 10 / 0 # ZeroDivisionError: division by zero

Example 2: Floor division by zero

result = 10 // 0 # ZeroDivisionError: integer division or modulo by zero

Example 3: Modulo by zero

result = 10 % 0 # ZeroDivisionError: integer division or modulo by zero

7.3 Common Scenarios

7.4 How to Handle a ZeroDivisionError

def safe_divide(numerator, denominator): try: return numerator / denominator except ZeroDivisionError: print("Error: Cannot divide by zero.") return None # Or some other sentinel value result = safe_divide(10, 0) if result is not None: print(f"Result: {result}")

7.5 Preventing ZeroDivisionError

  1. Always check the denominator:

    if denominator != 0: result = numerator / denominator else: print("Cannot divide by zero")
  2. Use a default value when denominator is zero:

    result = numerator / denominator if denominator != 0 else 0
  3. In data processing, check for empty datasets:

    if len(data) > 0: average = sum(data) / len(data) else: average = 0

8. FileNotFoundError: When Files Go Missing

8.1 What Causes a FileNotFoundError?

A FileNotFoundError is raised when you try to open or manipulate a file or directory that doesn't exist. It is a subclass of OSError.

8.2 Code Examples

Example 1: Opening a non-existent file

file = open("non_existent_file.txt", "r") # FileNotFoundError: [Errno 2] No such file or directory

Example 2: Removing a non-existent file

import os os.remove("non_existent_file.txt") # FileNotFoundError: [Errno 2] No such file or directory

Example 3: Accessing a directory that doesn't exist

import os os.listdir("non_existent_directory") # FileNotFoundError: [Errno 2] No such file or directory

8.3 Common Scenarios

8.4 How to Handle a FileNotFoundError

try: with open("config.txt", "r") as file: content = file.read() except FileNotFoundError: print("Error: The configuration file could not be found.") # You could create a default file or use default values

8.5 Preventing FileNotFoundError

  1. Check if a file exists before opening it:

    import os if os.path.exists("config.txt"): with open("config.txt", "r") as file: content = file.read() else: print("File not found")
  2. Use try...except for robust file operations.

  3. Provide clear error messages to help users locate the issue.

Exception Cause
FileNotFoundError File doesn't exist
PermissionError No permission to access the file
IsADirectoryError Expected a file, got a directory
NotADirectoryError Expected a directory, got a file
TimeoutError Operation timed out

9. OSError: When the Operating System Says No

9.1 What Causes an OSError?

An OSError is raised when a system-related operation (like file operations, I/O, or system calls) fails. It's the parent class for many system-related exceptions, including FileNotFoundError, PermissionError, and others.

9.2 Code Examples

Example 1: Opening a file without permission

file = open("/root/secret.txt", "r") # PermissionError (subclass of OSError)

Example 2: Creating a file in a read-only directory

file = open("/readonly_dir/newfile.txt", "w") # OSError: [Errno 30] Read-only file system

Example 3: Removing a file that is locked

import os os.remove("locked_file.txt") # OSError: [Errno 13] Permission denied

9.3 Common Scenarios

9.4 How to Handle an OSError

try: with open("important_data.txt", "w") as file: file.write("Sensitive data") except OSError as e: print(f"An operating system error occurred: {e}") print(f"Error number: {e.errno}") print(f"Error message: {e.strerror}")

9.5 OSError Subclasses

OSError β”œβ”€β”€ BlockingIOError β”œβ”€β”€ ChildProcessError β”œβ”€β”€ ConnectionError β”œβ”€β”€ FileExistsError β”œβ”€β”€ FileNotFoundError β”œβ”€β”€ InterruptedError β”œβ”€β”€ IsADirectoryError β”œβ”€β”€ NotADirectoryError β”œβ”€β”€ PermissionError β”œβ”€β”€ ProcessLookupError └── TimeoutError

9.6 Best Practices for Handling OSErrors

  1. Handle specific subclasses first, then fall back to OSError:

    try: # File operation except FileNotFoundError: print("File not found") except PermissionError: print("Permission denied") except OSError as e: print(f"Other OS error: {e}")
  2. Use error codes for fine-grained handling:

    try: # Some operation except OSError as e: if e.errno == 28: # No space left on device print("Disk is full") else: print(f"Unknown error: {e}")

10. RuntimeError: The Catch-All for Unclassifiable Errors

10.1 What Causes a RuntimeError?

A RuntimeError is a general-purpose exception that is raised when an error doesn't fall into any other specific category. It's often used when Python encounters an error condition that doesn't have a more specific exception class.

10.2 Code Examples

Example 1: Recursion depth exceeded

def recursive_function(): recursive_function() # Eventually raises RecursionError (subclass of RuntimeError) recursive_function()

Example 2: Dictionary changed during iteration

my_dict = {"a": 1, "b": 2} for key in my_dict: my_dict["c"] = 3 # RuntimeError: dictionary changed size during iteration

10.3 Common Scenarios

10.4 How to Handle a RuntimeError

try: # Some operation that might raise RuntimeError result = complex_operation() except RuntimeError as e: print(f"A runtime error occurred: {e}") # Log the error for debugging

10.5 Note About RuntimeError

RuntimeError is intentionally broad. In practice, you should catch more specific exceptions when possible. Use RuntimeError as a last resort for unexpected errors that aren't covered by other exception types.

11. AssertionError: When Assumptions Fail

11.1 What Causes an AssertionError?

An AssertionError is raised when an assert statement fails. The assert statement is a debugging tool that tests a condition. If the condition is False, an AssertionError is raised.

11.2 Code Examples

Example 1: Basic assertion failure

x = 5 assert x == 10 # AssertionError

Example 2: Assertion with an error message

age = -5 assert age >= 0, "Age cannot be negative" # AssertionError: Age cannot be negative

Example 3: Asserting in a function

def calculate_average(numbers): assert len(numbers) > 0, "List cannot be empty" return sum(numbers) / len(numbers) data = [] average = calculate_average(data) # AssertionError: List cannot be empty

11.3 When to Use Assert

DO use assert for:

DO NOT use assert for:

11.4 How to Handle an AssertionError

try: assert x == 10, "x should be 10" except AssertionError as e: print(f"Assertion failed: {e}")

11.5 Global Disablement of Assertions

Assertions can be globally disabled by running Python with the -O (optimize) flag:

python -O script.py

This means you should never rely on assertions for critical security or business logicβ€”use proper error handling instead.

11.6 AssertionError vs ValueError

Aspect AssertionError ValueError
Purpose Debugging, testing assumptions Handling invalid values
Can be disabled Yes (with -O flag) No
Use in production Not recommended Yes
Example assert age >= 0 raise ValueError("Age cannot be negative")

12. Exception Hierarchy Revisited

BaseException β”œβ”€β”€ SystemExit β”œβ”€β”€ KeyboardInterrupt β”œβ”€β”€ GeneratorExit └── Exception β”œβ”€β”€ NameError ← Undefined variable β”œβ”€β”€ TypeError ← Wrong type for operation β”œβ”€β”€ ValueError ← Wrong value (same type) β”œβ”€β”€ ArithmeticError β”‚ └── ZeroDivisionError ← Division by zero β”œβ”€β”€ LookupError β”‚ β”œβ”€β”€ IndexError β”‚ └── KeyError β”œβ”€β”€ OSError β”‚ β”œβ”€β”€ FileNotFoundError ← File doesn't exist β”‚ β”œβ”€β”€ PermissionError β”‚ └── ... β”œβ”€β”€ RuntimeError ← General purpose └── AssertionError ← Assert statement fails

12.1 Quick Reference Table

Exception Common Cause Example
NameError Undefined variable print(x) where x not defined
TypeError Operation on wrong type "hello" + 5
ValueError Right type, wrong value int("abc")
ZeroDivisionError Division by zero 10 / 0
FileNotFoundError File doesn't exist open("missing.txt")
OSError System-level error Permission denied, disk full
RuntimeError Unclassifiable error Dictionary changed during iteration
AssertionError Assert condition fails assert x == 10 when x is 5

13. Summary

  1. NameError: Python can't find a name you're trying to use. Check for typos, initialization, and scope.

  2. TypeError: You're using the wrong type for an operation. Convert types or check with isinstance().

  3. ValueError: The value is wrong for the operation (right type, wrong content). Validate values before use.

  4. ZeroDivisionError: You tried to divide by zero. Always check denominators.

  5. FileNotFoundError: The file you're trying to access doesn't exist. Check paths and file existence.

  6. OSError: A system-level operation failed. Handle specific subclasses when possible.

  7. RuntimeError: A catch-all for errors without a more specific class. Use specific exceptions when possible.

  8. AssertionError: An assert statement failed. Use only for debugging and testing, not for production validation.

  9. Exception Hierarchy: All exceptions inherit from Exception (which inherits from BaseException). Catch specific exceptions first.

  10. Best Practice: Always handle exceptions as specifically as possible. Use except Exception: only as a last resort.

14. Self-Assessment Quiz

Test your understanding of the concepts covered in this tutorial. Answer each question, then click to reveal the correct answer.

Q1: Which exception is raised when you try to use a variable that hasn't been defined?

Answer`NameError`

Q2: What exception would the following code raise?

result = "100" + 50
Answer(B) `TypeError`

Q3: True or False: ValueError and TypeError are the same type of exception.

AnswerFalse. `ValueError` is for wrong values; `TypeError` is for wrong types.

Q4: Which exception is a subclass of OSError?

Answer(C) `FileNotFoundError`

Q5: The following code raises a ValueError. Why?

number = int("12.5")
AnswerIt's a ValueError because "12.5" is not a valid integer literal (contains a decimal point). `int()` cannot parse floats represented as strings.

Q6: What is the difference between FileNotFoundError and OSError?

Answer`FileNotFoundError` is a specific subclass of `OSError` for file-not-found conditions. `OSError` covers all system-level errors.

Q7: Which exception would be raised by the following code?

my_list = [1, 2, 3] my_list.remove(5)
Answer(B) `ValueError` – `remove()` raises ValueError when the element is not in the list.

Q8: True or False: AssertionError can be disabled globally by running Python with the -O flag.

AnswerTrue. Assertions can be disabled with the `-O` optimize flag.

Q9: What exception is raised when you try to divide by zero?

Answer(C) `ZeroDivisionError`

Q10: Which of the following code snippets would correctly handle a FileNotFoundError?

Answer(D) Both B and C handle `FileNotFoundError` (C catches the parent class too).

15. Practical Exercises

Complete the exercises below to reinforce your understanding. Sample solutions are provided after each exercise.

Exercise 1: Identifying Exceptions

Instructions: For each code snippet, identify which exception will be raised and explain why.

a)

text = "Python" print(text[10])
Sample Answer`IndexError` (which is a subclass of `LookupError`). The index 10 is out of range for a string of length 6.

b)

def multiply(a, b): return a * b result = multiply("5", 3)
Sample AnswerThis actually works because `"5" * 3` repeats the string. A better example that raises `TypeError` would be `multiply("5", "3")` which raises `TypeError: can't multiply sequence by non-int of type 'str'`.

c)

import math result = math.sqrt(-16)
Sample Answer`ValueError`. `math.sqrt()` cannot compute the square root of a negative number (math domain error).

d)

numbers = [10, 20, 30] total = sum(numbers) average = total / len(numbers) print(average)

(Assume this runs without error. Then change numbers = [] and rerun.)

Sample AnswerWith `numbers = []`, this raises `ZeroDivisionError` because `len(numbers)` is 0.

e)

my_dict = {"name": "Alice", "age": 25} print(my_dict["city"])
Sample Answer`KeyError` (subclass of `LookupError`). The key `"city"` doesn't exist in the dictionary.

f)

value = 42 value.append(5)
Sample Answer`AttributeError`. Integers don't have an `append()` method.

g)

def factorial(n): if n < 0: raise ValueError("n must be non-negative") result = 1 for i in range(1, n + 1): result *= i return result print(factorial(-5))
Sample Answer`ValueError`. The function explicitly raises a `ValueError` when `n < 0`.

Exercise 2: Exception Handling

Instructions: Write a Python program that:

  1. Asks the user for two numbers.
  2. Divides the first number by the second number.
  3. Handles the following exceptions:
  4. Prints the result or an appropriate error message.
Sample Answer
def divide_numbers(): try: num1 = float(input("Enter the first number: ")) num2 = float(input("Enter the second number: ")) result = num1 / num2 print(f"{num1} / {num2} = {result}") except ValueError: print("Error: Please enter valid numeric values.") except ZeroDivisionError: print("Error: Cannot divide by zero.") except TypeError: print("Error: Unexpected type error occurred.") except Exception as e: print(f"An unexpected error occurred: {e}") # Run the function divide_numbers()

Exercise 3: File Operations with Exception Handling

Instructions: Write a function read_file_safe(filename) that:

  1. Attempts to open and read the contents of the file.
  2. Handles the following exceptions:
  3. Returns the file content as a string if successful, or None if an error occurs.

Test your function with: A non-existent file, a file you don't have permission to read, and a directory name.

Sample Answer
def read_file_safe(filename): try: with open(filename, "r") as file: return file.read() except FileNotFoundError: print(f"File not found: {filename}") except PermissionError: print(f"Permission denied: {filename}") except IsADirectoryError: print(f"Expected a file, but got a directory: {filename}") except OSError as e: print(f"An OS error occurred: {e}") except Exception as e: print(f"An unexpected error occurred: {e}") return None # Test the function print(read_file_safe("non_existent.txt")) # This would require setting up actual test files/directories

Exercise 4: Handling RuntimeError

Instructions: The following code raises a RuntimeError. Identify the error and write proper code that avoids it or handles it gracefully.

my_dict = {'a': 1, 'b': 2, 'c': 3} for key in my_dict: if key == 'b': my_dict['d'] = 4 print(key, my_dict[key])
Sample Answer

Problem: The error occurs because the dictionary is modified during iteration (adding a new key while iterating).

Fixed Version 1 – iterate over a copy of keys:

my_dict = {'a': 1, 'b': 2, 'c': 3} for key in list(my_dict.keys()): # Create a copy of keys if key == 'b': my_dict['d'] = 4 print(key, my_dict[key])

Fixed Version 2 – use exception handling:

my_dict = {'a': 1, 'b': 2, 'c': 3} try: for key in my_dict: if key == 'b': my_dict['d'] = 4 print(key, my_dict[key]) except RuntimeError as e: print(f"Error: {e}") print("Hint: Don't modify a dictionary while iterating over it.")

Exercise 5: Custom Validation with AssertionError

Instructions: Write a function validate_age(age) that:

  1. Uses assert to check that age is an integer.
  2. Uses assert to check that age is between 0 and 150.
  3. If both assertions pass, returns the age.
  4. Handles any AssertionError that occurs by printing "Invalid age" and returning None.

Test the function with:

Sample Answer
def validate_age(age): try: assert isinstance(age, int), "Age must be an integer" assert 0 <= age <= 150, "Age must be between 0 and 150" return age except AssertionError as e: print(f"Invalid age: {e}") return None # Test the function print(validate_age(25)) # Output: 25 print(validate_age(-5)) # Output: Invalid age: Age must be between 0 and 150, None print(validate_age(200)) # Output: Invalid age: Age must be between 0 and 150, None print(validate_age("25")) # Output: Invalid age: Age must be an integer, None

16. Homework Questions

Short Answer Questions

1. For each of the following scenarios, identify the most specific exception that would be raised and explain why. Then, write a try...except block that handles that specific exception.

a) You try to open a file for reading, but the file doesn't exist in the current directory.

Sample Answer

Exception: FileNotFoundError

try: with open("missing_file.txt", "r") as f: content = f.read() except FileNotFoundError: print("The file could not be found.")

b) You try to add an integer and a string together.

Sample Answer

Exception: TypeError

try: result = "Hello" + 5 except TypeError: print("Cannot add string and integer.")

c) You try to convert the string "hello" to an integer.

Sample Answer

Exception: ValueError

try: number = int("hello") except ValueError: print("Invalid literal for integer conversion.")

d) You try to access the 10th element of a list that has only 3 elements.

Sample Answer

Exception: IndexError

my_list = [1, 2, 3] try: print(my_list[10]) except IndexError: print("Index out of range.")

e) You try to divide a number by zero.

Sample Answer

Exception: ZeroDivisionError

try: result = 10 / 0 except ZeroDivisionError: print("Cannot divide by zero.")

2. The following code has poor exception handling. Rewrite it to follow best practices:

try: filename = input("Enter filename: ") file = open(filename, "r") content = file.read() number = int(content) result = 100 / number print(result) file.close() except: print("An error occurred")

Requirements for your rewrite:

  1. Handle specific exceptions (at least 4 different types).
  2. Provide meaningful error messages for each.
  3. Use else and finally where appropriate.
  4. Use the with statement for file handling.
Sample Answer
def process_file(): try: filename = input("Enter filename: ") try: with open(filename, "r") as file: content = file.read() except FileNotFoundError: print(f"Error: File '{filename}' not found. Please check the filename and path.") return except PermissionError: print(f"Error: Permission denied to read '{filename}'.") return except IsADirectoryError: print(f"Error: '{filename}' is a directory, not a file.") return except OSError as e: print(f"Error: OS error while opening file: {e}") return try: number = int(content.strip()) except ValueError: print(f"Error: File content '{content.strip()}' is not a valid integer.") return try: result = 100 / number except ZeroDivisionError: print("Error: File content was zero, cannot divide by zero.") return except TypeError: print("Error: Unexpected type error during division.") return print(f"Result: {result}") except Exception as e: print(f"An unexpected error occurred: {e}") # Run the function process_file()

Essay Question

3. Research the following exceptions and write a paragraph about each: RecursionError, KeyError, AttributeError, ImportError, StopIteration. For each exception, provide:

Sample Answer

RecursionError:

KeyError:

AttributeError:

ImportError:

StopIteration:

Research Question

4. Design a simple logging system that writes error messages to a file. The system should:

  1. Define a function log_error(message, filename="error_log.txt") that:

  2. Write a test program that:

Sample Answer
import datetime def log_error(message, filename="error_log.txt"): timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") log_entry = f"[{timestamp}] {message}\n" try: with open(filename, "a") as log_file: log_file.write(log_entry) except FileNotFoundError: # File doesn't exist, create it with open(filename, "w") as log_file: log_file.write(log_entry) except PermissionError: print(f"WARNING: Cannot write to log file ({filename}). Printing to console:") print(log_entry.strip()) except OSError as e: print(f"WARNING: OS error while writing to log file: {e}") print(log_entry.strip()) except Exception as e: print(f"WARNING: Unexpected error while writing to log: {e}") def test_logging(): try: # Test with a non-existent file with open("missing.txt", "r"): pass except FileNotFoundError as e: log_error(f"File not found: {e}") try: # Test division by zero x = 10 / 0 except ZeroDivisionError as e: log_error(f"Division by zero: {e}") try: # Test invalid conversion x = int("abc") except ValueError as e: log_error(f"Value error: {e}") print("All errors logged. Check error_log.txt") if __name__ == "__main__": test_logging()

This tutorial provides a comprehensive understanding of the most common built-in exceptions in Python. The quizzes, exercises, and homework problems will help reinforce the concepts and prepare you for real-world programming scenarios. Happy learning!

Previous | Tutorial index | Next