================================================================================ GMAIL CLEANUP - CHALLENGE 3 SOLUTION Chapter 2: Configuration Management Challenge: Error Handling & Logging ================================================================================ PROBLEM: Enhance config loading with error handling: 1. Catch missing .env file gracefully 2. Handle type conversion errors 3. Print helpful error messages 4. Test with missing/invalid values SOLUTION: ================================================================================ File: config.py (final version with error handling) import os import re import sys from pathlib import Path from dotenv import load_dotenv class Config: """Application configuration with comprehensive error handling.""" def __init__(self): self.valid = False self.errors = [] # Try to load .env file env_file = Path(__file__).parent / ".env" if not env_file.exists(): print(f"⚠️ .env not found at {env_file}") print(" Copy .env.example to .env and edit it") return try: load_dotenv(env_file) except Exception as e: self.errors.append(f"Failed to load .env: {e}") return # Load variables with error handling try: self.gmail_user = os.environ.get('GMAIL_USER') self.api_timeout = int(os.environ.get('API_REQUEST_TIMEOUT', '10')) self.max_results = int(os.environ.get('MAX_RESULTS', '100')) self.days_old = int(os.environ.get('DELETE_OLDER_THAN_DAYS', '365')) self.min_attachment_mb = int(os.environ.get('MIN_ATTACHMENT_SIZE_MB', '10')) except ValueError as e: self.errors.append(f"Configuration value is not a number: {e}") return except Exception as e: self.errors.append(f"Unexpected error loading config: {e}") return self.valid = True def validate(self) -> bool: """Validate configuration after loading.""" if self.errors: for error in self.errors: print(f"❌ {error}") return False # Email validation if not self.gmail_user: print("❌ GMAIL_USER is empty") return False email_pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' if not re.match(email_pattern, self.gmail_user): print(f"❌ GMAIL_USER '{self.gmail_user}' is invalid") return False # Numeric validation if self.api_timeout <= 0: print("❌ API_REQUEST_TIMEOUT must be positive") return False if self.max_results <= 0: print("❌ MAX_RESULTS must be positive") return False print("✅ Configuration is valid!") return True def main(): """Test configuration loading.""" print("=" * 60) print("CONFIGURATION LOADER TEST") print("=" * 60) config = Config() if not config.valid: print("\n❌ Failed to load configuration") return False if not config.validate(): print("\n❌ Configuration validation failed") return False print(f"\nConfiguration loaded:") print(f" Gmail User: {config.gmail_user}") print(f" API Timeout: {config.api_timeout}s") print(f" Max Results: {config.max_results}") print(f" Delete emails older than: {config.days_old} days") return True if __name__ == "__main__": success = main() sys.exit(0 if success else 1) ERROR HANDLING SCENARIOS ================================================================================ 1. MISSING .ENV FILE Error: ⚠️ .env not found at /path/to/.env Copy .env.example to .env and edit it 2. INVALID EMAIL Error: ❌ GMAIL_USER 'notanemail' is invalid 3. TYPE CONVERSION ERROR Error: ❌ Configuration value is not a number: invalid literal for int() 4. EMPTY VALUE Error: ❌ GMAIL_USER is empty TESTING ALL SCENARIOS ================================================================================ # Scenario 1: Valid config python config.py Expected: ✅ Configuration is valid! # Scenario 2: Missing .env rm .env python config.py Expected: ⚠️ .env not found # Scenario 3: Invalid email in .env GMAIL_USER=notanemail python config.py Expected: ❌ GMAIL_USER 'notanemail' is invalid # Scenario 4: Non-numeric timeout API_REQUEST_TIMEOUT=abc python config.py Expected: ❌ Configuration value is not a number KEY PATTERNS ================================================================================ ✅ Try/except for file I/O ✅ Try/except for type conversion ✅ Graceful error messages ✅ Early return on failure ✅ Helpful hints for fixing This is production-ready error handling! ================================================================================