1
Fork 0
crypto_bot_training/Session_02/error-handling-examples.ipynb
2025-06-13 20:00:45 +02:00

509 lines
16 KiB
Text

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Python Error Handling - try, except, and Beyond\n",
"\n",
"This notebook covers Python's error handling mechanisms from basic try/except blocks to advanced exception handling patterns.\n",
"\n",
"## Learning Objectives\n",
"- Understand Python's exception hierarchy\n",
"- Master try, except, else, and finally blocks\n",
"- Learn to handle multiple exception types\n",
"- Create and use custom exceptions\n",
"- Follow error handling best practices"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 1. Understanding Exceptions\n",
"\n",
"An **exception** is an event that occurs during program execution that disrupts the normal flow of instructions."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Common exceptions without handling\n",
"print(\"=== Common Exception Types ===\")\n",
"\n",
"def show_exceptions():\n",
" examples = [\n",
" (\"ZeroDivisionError\", lambda: 10 / 0),\n",
" (\"IndexError\", lambda: [1, 2, 3][5]),\n",
" (\"KeyError\", lambda: {'a': 1}['b']),\n",
" (\"TypeError\", lambda: \"hello\" + 5),\n",
" (\"ValueError\", lambda: int(\"not_a_number\"))\n",
" ]\n",
" \n",
" for name, func in examples:\n",
" try:\n",
" func()\n",
" except Exception as e:\n",
" print(f\"❌ {name}: {e}\")\n",
"\n",
"show_exceptions()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 2. Basic try/except Syntax"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Basic try/except structure\n",
"def safe_divide(a, b):\n",
" try:\n",
" result = a / b\n",
" print(f\"✅ {a} ÷ {b} = {result}\")\n",
" return result\n",
" except ZeroDivisionError:\n",
" print(f\"❌ Cannot divide {a} by zero!\")\n",
" return None\n",
"\n",
"# Test the function\n",
"safe_divide(10, 2)\n",
"safe_divide(10, 0)\n",
"\n",
"print()\n",
"\n",
"# Capturing exception details\n",
"def analyze_exception(func):\n",
" try:\n",
" result = func()\n",
" print(f\"✅ Success: {result}\")\n",
" except Exception as e:\n",
" print(f\"❌ {type(e).__name__}: {e}\")\n",
"\n",
"analyze_exception(lambda: 10 / 2) # Success\n",
"analyze_exception(lambda: 10 / 0) # ZeroDivisionError\n",
"analyze_exception(lambda: int(\"abc\")) # ValueError"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 3. Multiple Exception Types"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Handling different exceptions separately\n",
"def robust_calculator(expression):\n",
" try:\n",
" result = eval(expression) # Note: eval is dangerous in real applications!\n",
" print(f\"✅ {expression} = {result}\")\n",
" return result\n",
" \n",
" except ZeroDivisionError:\n",
" print(f\"❌ Division by zero in: {expression}\")\n",
" \n",
" except NameError as e:\n",
" print(f\"❌ Undefined variable in: {expression}\")\n",
" \n",
" except (TypeError, ValueError) as e:\n",
" print(f\"❌ Type/Value error in: {expression} - {e}\")\n",
"\n",
"# Test with various expressions\n",
"test_expressions = [\"10 + 5\", \"20 / 0\", \"x + 5\", \"'hello' + 5\"]\n",
"\n",
"for expr in test_expressions:\n",
" robust_calculator(expr)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 4. The Complete try/except/else/finally Structure"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Complete structure demonstration\n",
"def file_processor(filename):\n",
" file_handle = None\n",
" \n",
" try:\n",
" print(f\"📂 Opening: {filename}\")\n",
" file_handle = open(filename, 'r')\n",
" content = file_handle.read()\n",
" print(f\"📖 Read {len(content)} characters\")\n",
" \n",
" except FileNotFoundError:\n",
" print(f\"❌ File not found: {filename}\")\n",
" return None\n",
" \n",
" except PermissionError:\n",
" print(f\"❌ Permission denied: {filename}\")\n",
" return None\n",
" \n",
" else:\n",
" # Runs only if no exception occurred\n",
" print(\"✅ File processing successful\")\n",
" return content\n",
" \n",
" finally:\n",
" # Always runs for cleanup\n",
" if file_handle and not file_handle.closed:\n",
" print(\"🔒 Closing file\")\n",
" file_handle.close()\n",
"\n",
"# Create a test file\n",
"with open(\"test.txt\", \"w\") as f:\n",
" f.write(\"Hello, World!\")\n",
"\n",
"# Test with existing and non-existing files\n",
"file_processor(\"test.txt\")\n",
"print()\n",
"file_processor(\"nonexistent.txt\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 5. Raising Exceptions"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Raising exceptions manually\n",
"def validate_age(age):\n",
" if not isinstance(age, (int, float)):\n",
" raise TypeError(f\"Age must be a number, got {type(age).__name__}\")\n",
" \n",
" if age < 0:\n",
" raise ValueError(\"Age cannot be negative\")\n",
" \n",
" if age > 150:\n",
" raise ValueError(\"Age seems unrealistic (over 150)\")\n",
" \n",
" print(f\"✅ Valid age: {age}\")\n",
" return age\n",
"\n",
"# Test age validation\n",
"test_ages = [25, -5, \"thirty\", 200]\n",
"\n",
"for age in test_ages:\n",
" try:\n",
" validate_age(age)\n",
" except (TypeError, ValueError) as e:\n",
" print(f\"❌ {type(e).__name__}: {e}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 6. Custom Exceptions"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Creating custom exception classes\n",
"class BankingError(Exception):\n",
" \"\"\"Base exception for banking operations\"\"\"\n",
" def __init__(self, message, account_id=None):\n",
" super().__init__(message)\n",
" self.account_id = account_id\n",
"\n",
"class InsufficientFundsError(BankingError):\n",
" \"\"\"Raised when account has insufficient funds\"\"\"\n",
" def __init__(self, required, available, account_id):\n",
" message = f\"Need ${required}, have ${available}\"\n",
" super().__init__(message, account_id)\n",
" self.required = required\n",
" self.available = available\n",
"\n",
"class AccountFrozenError(BankingError):\n",
" \"\"\"Raised when account is frozen\"\"\"\n",
" pass\n",
"\n",
"# Banking system using custom exceptions\n",
"class BankAccount:\n",
" def __init__(self, account_id, balance=0):\n",
" self.account_id = account_id\n",
" self.balance = balance\n",
" self.is_frozen = False\n",
" \n",
" def withdraw(self, amount):\n",
" if self.is_frozen:\n",
" raise AccountFrozenError(f\"Account {self.account_id} is frozen\", self.account_id)\n",
" \n",
" if amount > self.balance:\n",
" raise InsufficientFundsError(amount, self.balance, self.account_id)\n",
" \n",
" self.balance -= amount\n",
" print(f\"✅ Withdrew ${amount}. New balance: ${self.balance}\")\n",
"\n",
"# Test the banking system\n",
"account = BankAccount(\"ACC001\", 100)\n",
"\n",
"try:\n",
" account.withdraw(50) # Should work\n",
" account.withdraw(100) # Should fail - insufficient funds\n",
"except BankingError as e:\n",
" print(f\"❌ Banking error: {e}\")\n",
" print(f\" Account: {e.account_id}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 7. Exception Chaining"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Exception chaining with 'from' keyword\n",
"class DataProcessingError(Exception):\n",
" pass\n",
"\n",
"def load_config(filename):\n",
" try:\n",
" with open(filename, 'r') as f:\n",
" import json\n",
" return json.loads(f.read())\n",
" except FileNotFoundError as e:\n",
" raise DataProcessingError(f\"Config file missing: {filename}\") from e\n",
" except json.JSONDecodeError as e:\n",
" raise DataProcessingError(f\"Invalid JSON in: {filename}\") from e\n",
"\n",
"def process_data(config_file):\n",
" try:\n",
" config = load_config(config_file)\n",
" print(f\"✅ Loaded config: {config}\")\n",
" except DataProcessingError as e:\n",
" print(f\"❌ Processing failed: {e}\")\n",
" print(f\" Original cause: {e.__cause__}\")\n",
"\n",
"# Create test files\n",
"import json\n",
"with open(\"valid.json\", \"w\") as f:\n",
" json.dump({\"setting\": \"value\"}, f)\n",
"\n",
"with open(\"invalid.json\", \"w\") as f:\n",
" f.write(\"{ invalid json }\")\n",
"\n",
"# Test exception chaining\n",
"process_data(\"valid.json\") # Should work\n",
"process_data(\"missing.json\") # FileNotFoundError -> DataProcessingError\n",
"process_data(\"invalid.json\") # JSONDecodeError -> DataProcessingError"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 8. Best Practices"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Best practices demonstration\n",
"import logging\n",
"from functools import wraps\n",
"\n",
"logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(message)s')\n",
"logger = logging.getLogger(__name__)\n",
"\n",
"# ✅ Good: Specific exception handling\n",
"def good_file_reader(filename):\n",
" try:\n",
" with open(filename, 'r') as f:\n",
" content = f.read()\n",
" logger.info(f\"Read {filename} successfully\")\n",
" return content\n",
" except FileNotFoundError:\n",
" logger.warning(f\"File not found: {filename}\")\n",
" return None\n",
" except PermissionError:\n",
" logger.error(f\"Permission denied: {filename}\")\n",
" return None\n",
" except Exception as e:\n",
" logger.error(f\"Unexpected error: {e}\")\n",
" raise # Re-raise unexpected errors\n",
"\n",
"# Exception handling decorator\n",
"def handle_exceptions(default_return=None, log_errors=True):\n",
" def decorator(func):\n",
" @wraps(func)\n",
" def wrapper(*args, **kwargs):\n",
" try:\n",
" return func(*args, **kwargs)\n",
" except Exception as e:\n",
" if log_errors:\n",
" logger.error(f\"Error in {func.__name__}: {e}\")\n",
" return default_return\n",
" return wrapper\n",
" return decorator\n",
"\n",
"@handle_exceptions(default_return=0)\n",
"def safe_divide(a, b):\n",
" return a / b\n",
"\n",
"# Test best practices\n",
"print(\"=== Testing Best Practices ===\")\n",
"result = good_file_reader(\"test.txt\")\n",
"print(f\"File content: {result[:20] if result else 'None'}...\")\n",
"\n",
"print(f\"Safe divide 10/2: {safe_divide(10, 2)}\")\n",
"print(f\"Safe divide 10/0: {safe_divide(10, 0)}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 9. Debugging Exceptions"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Using traceback for debugging\n",
"import traceback\n",
"import sys\n",
"\n",
"def debug_function():\n",
" def level_1():\n",
" return level_2()\n",
" \n",
" def level_2():\n",
" data = {\"key\": \"value\"}\n",
" return data[\"missing_key\"] # KeyError\n",
" \n",
" try:\n",
" level_1()\n",
" except Exception as e:\n",
" print(f\"❌ Exception: {e}\")\n",
" print(\"\\n🔍 Traceback:\")\n",
" traceback.print_exc()\n",
" \n",
" # Get exception info\n",
" exc_type, exc_value, exc_traceback = sys.exc_info()\n",
" print(f\"\\n📊 Exception details:\")\n",
" print(f\" Type: {exc_type.__name__}\")\n",
" print(f\" Value: {exc_value}\")\n",
"\n",
"debug_function()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Summary\n",
"\n",
"### Key Concepts Covered:\n",
"\n",
"🎯 **Basic Structure:**\n",
"- `try`: Code that might raise an exception\n",
"- `except`: Handle specific exceptions\n",
"- `else`: Runs only if no exception occurred\n",
"- `finally`: Always runs for cleanup\n",
"\n",
"🛠️ **Advanced Features:**\n",
"- Multiple exception handling\n",
"- Custom exception classes\n",
"- Exception chaining with `from`\n",
"- Re-raising exceptions with `raise`\n",
"\n",
"📋 **Best Practices:**\n",
"- Use specific exception types\n",
"- Don't suppress exceptions silently\n",
"- Log errors appropriately\n",
"- Clean up resources properly\n",
"- Create meaningful custom exceptions\n",
"\n",
"### Exception Hierarchy (Common Types):\n",
"```\n",
"Exception\n",
" ├── ArithmeticError\n",
" │ └── ZeroDivisionError\n",
" ├── AttributeError\n",
" ├── LookupError\n",
" │ ├── IndexError\n",
" │ └── KeyError\n",
" ├── NameError\n",
" ├── OSError\n",
" │ ├── FileNotFoundError\n",
" │ └── PermissionError\n",
" ├── TypeError\n",
" └── ValueError\n",
"```\n",
"\n",
"### Remember:\n",
"- **Catch specific exceptions** rather than using broad `except Exception`\n",
"- **Always clean up resources** using `finally` or context managers\n",
"- **Log errors meaningfully** to help with debugging\n",
"- **Don't hide failures** - make them visible and actionable"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "venv",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.3"
}
},
"nbformat": 4,
"nbformat_minor": 4
}