ERROR HANDLING - Robust file operations with error handling
Python
#!/usr/bin/env python3
"""
ERROR HANDLING - Robust file operations with error handling
Demonstrates handling various file-related errors
"""
import os
import tempfile
import errno
print("=" * 60)
print("ERROR HANDLING - Robust File Operations")
print("=" * 60)
temp_dir = tempfile.gettempdir()
# Example 1: FileNotFoundError
print("\n1. Handling FileNotFoundError")
print("-" * 40)
nonexistent = os.path.join(temp_dir, "does_not_exist.txt")
try:
with open(nonexistent, 'r') as f:
content = f.read()
except FileNotFoundError:
print(f" Error: File not found: {nonexistent}")
print(" Creating file instead...")
with open(nonexistent, 'w') as f:
f.write("Now it exists\n")
print(" File created")
os.remove(nonexistent)
# Example 2: PermissionError
print("\n2. Handling PermissionError")
print("-" * 40)
readonly_file = os.path.join(temp_dir, "readonly.txt")
# Create and make read-only
with open(readonly_file, 'w') as f:
f.write("Read-only content\n")
os.chmod(readonly_file, 0o444) # Read-only
try:
with open(readonly_file, 'w') as f:
f.write("Try to write\n")
except PermissionError:
print(f" Error: Permission denied")
print(" File is read-only")
# Restore permissions and remove
os.chmod(readonly_file, 0o644)
os.remove(readonly_file)
# Example 3: IsADirectoryError
print("\n3. Handling IsADirectoryError")
print("-" * 40)
test_dir = os.path.join(temp_dir, "test_directory")
os.makedirs(test_dir, exist_ok=True)
try:
with open(test_dir, 'r') as f:
content = f.read()
except IsADirectoryError:
print(f" Error: {test_dir} is a directory, not a file")
print(" Listing directory instead:")
print(f" Contents: {os.listdir(test_dir)}")
os.rmdir(test_dir)
# Example 4: UnicodeDecodeError
print("\n4. Handling UnicodeDecodeError")
print("-" * 40)
binary_file = os.path.join(temp_dir, "binary.dat")
# Write binary data
with open(binary_file, 'wb') as f:
f.write(bytes([0xFF, 0xFE, 0xFD, 0xFC]))
try:
with open(binary_file, 'r', encoding='ascii') as f:
content = f.read()
except UnicodeDecodeError as e:
print(f" Error: Cannot decode as ASCII")
print(f" Position: {e.start}-{e.end}")
print(" Reading as binary instead:")
with open(binary_file, 'rb') as f:
data = f.read()
print(f" Binary data: {list(data)}")
os.remove(binary_file)
# Example 5: IOError / OSError
print("\n5. Handling IOError / OSError")
print("-" * 40)
test_file = os.path.join(temp_dir, "iotest.txt")
try:
with open(test_file, 'w') as f:
f.write("Test data\n")
# Try to write to closed file
with open(test_file, 'r') as f:
f.write("This will fail")
except (IOError, OSError) as e:
print(f" Error: {e}")
print(" Cannot write to file opened in read mode")
os.remove(test_file)
# Example 6: ValueError (invalid mode)
print("\n6. Handling ValueError (Invalid Mode)")
print("-" * 40)
test_file = os.path.join(temp_dir, "modetest.txt")
try:
with open(test_file, 'z') as f: # Invalid mode
pass
except ValueError as e:
print(f" Error: {e}")
print(" Valid modes: 'r', 'w', 'a', 'x', 'b', 't', '+'")
# Example 7: FileExistsError (exclusive creation)
print("\n7. Handling FileExistsError (Mode 'x')")
print("-" * 40)
existing_file = os.path.join(temp_dir, "existing.txt")
# Create file
with open(existing_file, 'w') as f:
f.write("Already exists\n")
try:
# Try to create exclusively
with open(existing_file, 'x') as f:
f.write("New content\n")
except FileExistsError:
print(f" Error: File already exists")
print(" Using append mode instead:")
with open(existing_file, 'a') as f:
f.write("Appended content\n")
os.remove(existing_file)
# Example 8: Comprehensive error handling
print("\n8. Comprehensive Error Handling")
print("-" * 40)
def safe_read_file(filepath):
"""Safely read file with comprehensive error handling"""
try:
with open(filepath, 'r') as f:
return f.read()
except FileNotFoundError:
return f"ERROR: File not found: {filepath}"
except PermissionError:
return f"ERROR: Permission denied: {filepath}"
except IsADirectoryError:
return f"ERROR: Is a directory: {filepath}"
except UnicodeDecodeError:
return f"ERROR: Cannot decode file: {filepath}"
except Exception as e:
return f"ERROR: Unexpected error: {e}"
# Test with various scenarios
test_file = os.path.join(temp_dir, "test.txt")
with open(test_file, 'w') as f:
f.write("Success!\n")
result = safe_read_file(test_file)
print(f" Valid file: {result.strip()}")
result = safe_read_file("/nonexistent/path/file.txt")
print(f" {result}")
os.remove(test_file)
# Example 9: Using errno for specific errors
print("\n9. Using errno for Specific Error Codes")
print("-" * 40)
def handle_file_error(filepath):
"""Handle errors using errno codes"""
try:
with open(filepath, 'r') as f:
return f.read()
except OSError as e:
if e.errno == errno.ENOENT:
print(f" Error code {errno.ENOENT}: File not found")
elif e.errno == errno.EACCES:
print(f" Error code {errno.EACCES}: Permission denied")
else:
print(f" Error code {e.errno}: {e}")
handle_file_error("/nonexistent.txt")
# Example 10: Cleanup with finally
print("\n10. Cleanup with Finally Block")
print("-" * 40)
test_file = os.path.join(temp_dir, "finally_test.txt")
f = None
try:
f = open(test_file, 'w')
f.write("Data\n")
print(" File written successfully")
except Exception as e:
print(f" Error: {e}")
finally:
if f is not None:
f.close()
print(" File closed in finally block")
os.remove(test_file)
# Example 11: Context manager is safer
print("\n11. Why Context Managers Are Better")
print("-" * 40)
# Without context manager - risky
try:
f = open(os.path.join(temp_dir, "risk.txt"), 'w')
f.write("Data\n")
# If error occurs here, file won't close
# 1 / 0 # Simulate error
f.close()
except Exception as e:
print(f" Error without context manager: {e}")
# With context manager - safe
try:
with open(os.path.join(temp_dir, "safe.txt"), 'w') as f:
f.write("Data\n")
# File closes automatically even on error
# 1 / 0 # Simulate error
except Exception as e:
print(f" Error with context manager: {e}")
print(" Context manager ensures file is closed")
# Cleanup
for f in [os.path.join(temp_dir, "risk.txt"),
os.path.join(temp_dir, "safe.txt")]:
if os.path.exists(f):
os.remove(f)
# Example 12: Error recovery strategies
print("\n12. Error Recovery Strategies")
print("-" * 40)
def read_with_fallback(primary, fallback):
"""Try primary file, fall back to secondary"""
try:
with open(primary, 'r') as f:
print(f" Reading from primary: {primary}")
return f.read()
except FileNotFoundError:
print(f" Primary not found, trying fallback")
try:
with open(fallback, 'r') as f:
print(f" Reading from fallback: {fallback}")
return f.read()
except FileNotFoundError:
print(f" Both files not found")
return None
# Create fallback file
fallback_file = os.path.join(temp_dir, "fallback.txt")
with open(fallback_file, 'w') as f:
f.write("Fallback data\n")
result = read_with_fallback(
os.path.join(temp_dir, "primary.txt"),
fallback_file
)
os.remove(fallback_file)
print("\n" + "=" * 60)
print("Key Points:")
print(" - Always use try/except for file operations")
print(" - Handle specific exceptions (FileNotFoundError, etc.)")
print(" - Context managers ensure cleanup")
print(" - Provide fallback strategies")
print(" - Use errno for detailed error codes")
print("=" * 60)