python

exercises

exercises.py🐍
"""
12 - Real Development Modules: Exercises
Practice os, sys, logging, argparse, pathlib.
"""

print("=" * 60)
print("REAL DEVELOPMENT MODULES EXERCISES")
print("=" * 60)

import os
import sys
from pathlib import Path
import logging
import argparse
import tempfile

# =============================================================================
# EXERCISE 1: Directory Explorer
# Create a function that takes a path and prints all files
# with their sizes in a formatted way.
# =============================================================================
print("\n--- Exercise 1: Directory Explorer ---")

# Your code here:


# Test:
# explore_directory('/path/to/folder')


# =============================================================================
# EXERCISE 2: Find Large Files
# Create a function that finds all files larger than a given size
# in a directory (recursively).
# =============================================================================
print("\n--- Exercise 2: Find Large Files ---")

# Your code here:


# Test:
# large_files = find_large_files('/path', min_size_bytes=1000000)


# =============================================================================
# EXERCISE 3: Environment Configuration
# Create a function that reads configuration from environment variables
# with defaults and type conversion.
# =============================================================================
print("\n--- Exercise 3: Environment Config ---")

# Your code here:


# Test:
# config = get_config()
# print(config)


# =============================================================================
# EXERCISE 4: Custom Logger
# Create a setup_logger function that creates a logger with both
# console and file handlers with different formats.
# =============================================================================
print("\n--- Exercise 4: Custom Logger ---")

# Your code here:


# Test:
# logger = setup_logger('myapp', 'app.log')
# logger.info("Test message")


# =============================================================================
# EXERCISE 5: File Organizer
# Create a function that organizes files in a directory by extension
# into subdirectories (e.g., .py -> python/, .txt -> text/).
# =============================================================================
print("\n--- Exercise 5: File Organizer ---")

# Your code here:


# Test:
# organize_by_extension('/path/to/messy/folder')


# =============================================================================
# EXERCISE 6: Path Statistics
# Create a function that returns statistics about paths in a directory:
# - Total files
# - Total directories
# - Total size
# - File types (by extension)
# =============================================================================
print("\n--- Exercise 6: Path Statistics ---")

# Your code here:


# Test:
# stats = get_path_stats('/path/to/folder')


# =============================================================================
# EXERCISE 7: Argparse CLI Tool
# Create a CLI tool with subcommands: create, delete, list
# for managing a simple todo list stored in a JSON file.
# =============================================================================
print("\n--- Exercise 7: CLI Todo Tool ---")

# Your code here:


# Usage:
# python script.py create "Buy groceries"
# python script.py list
# python script.py delete 1


# =============================================================================
# EXERCISE 8: Log Parser
# Create a function that parses log files and extracts:
# - Error counts by hour
# - Most common error messages
# - First and last log timestamps
# =============================================================================
print("\n--- Exercise 8: Log Parser ---")

# Your code here:


# Test:
# result = parse_logs('application.log')


# =============================================================================
# SOLUTIONS
# =============================================================================
print("\n\n" + "=" * 60)
print("SOLUTIONS")
print("=" * 60)

# SOLUTION 1
print("\n--- Solution 1: Directory Explorer ---")

def explore_directory(path):
    """Print all files in directory with their sizes."""
    path = Path(path)
    if not path.exists():
        print(f"Path not found: {path}")
        return
    
    total_size = 0
    for item in sorted(path.iterdir()):
        if item.is_file():
            size = item.stat().st_size
            total_size += size
            print(f"  {item.name:<30} {size:>10,} bytes")
        else:
            print(f"  {item.name:<30} [DIR]")
    
    print(f"\nTotal size: {total_size:,} bytes")

# Demo with temp directory
with tempfile.TemporaryDirectory() as tmpdir:
    tmp = Path(tmpdir)
    (tmp / 'file1.txt').write_text('Hello' * 100)
    (tmp / 'file2.py').write_text('print("world")')
    (tmp / 'subdir').mkdir()
    
    explore_directory(tmpdir)

# SOLUTION 2
print("\n--- Solution 2: Find Large Files ---")

def find_large_files(path, min_size_bytes=1000):
    """Find files larger than min_size_bytes."""
    path = Path(path)
    large_files = []
    
    for file in path.rglob('*'):
        if file.is_file():
            size = file.stat().st_size
            if size >= min_size_bytes:
                large_files.append((file, size))
    
    return sorted(large_files, key=lambda x: x[1], reverse=True)

# Demo
with tempfile.TemporaryDirectory() as tmpdir:
    tmp = Path(tmpdir)
    (tmp / 'small.txt').write_text('small')
    (tmp / 'large.txt').write_text('x' * 2000)
    (tmp / 'subdir').mkdir()
    (tmp / 'subdir' / 'huge.txt').write_text('y' * 5000)
    
    large = find_large_files(tmpdir, min_size_bytes=1000)
    print("Large files:")
    for file, size in large:
        print(f"  {file.name}: {size} bytes")

# SOLUTION 3
print("\n--- Solution 3: Environment Config ---")

def get_config():
    """Get configuration from environment variables."""
    config = {
        'debug': os.environ.get('DEBUG', 'false').lower() == 'true',
        'port': int(os.environ.get('PORT', '8080')),
        'host': os.environ.get('HOST', 'localhost'),
        'database_url': os.environ.get('DATABASE_URL', 'sqlite:///db.sqlite'),
        'log_level': os.environ.get('LOG_LEVEL', 'INFO'),
    }
    return config

# Demo
os.environ['DEBUG'] = 'true'
os.environ['PORT'] = '3000'
config = get_config()
print(f"Config: {config}")

# SOLUTION 4
print("\n--- Solution 4: Custom Logger ---")

def setup_logger(name, log_file=None, level=logging.DEBUG):
    """Create a logger with console and optional file handler."""
    logger = logging.getLogger(name)
    logger.setLevel(level)
    logger.handlers = []  # Clear existing handlers
    
    # Console handler
    console_handler = logging.StreamHandler()
    console_handler.setLevel(logging.INFO)
    console_format = logging.Formatter('%(levelname)s - %(message)s')
    console_handler.setFormatter(console_format)
    logger.addHandler(console_handler)
    
    # File handler
    if log_file:
        file_handler = logging.FileHandler(log_file)
        file_handler.setLevel(logging.DEBUG)
        file_format = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
        file_handler.setFormatter(file_format)
        logger.addHandler(file_handler)
    
    return logger

# Demo
with tempfile.NamedTemporaryFile(suffix='.log', delete=False) as f:
    log_file = f.name

logger = setup_logger('myapp', log_file)
logger.debug("Debug message")
logger.info("Info message")
logger.warning("Warning message")

print(f"\nLog file contents:")
print(Path(log_file).read_text())
os.remove(log_file)

# SOLUTION 5
print("\n--- Solution 5: File Organizer ---")

def organize_by_extension(path, dry_run=True):
    """Organize files into subdirectories by extension."""
    path = Path(path)
    
    extension_map = {
        '.py': 'python',
        '.txt': 'text',
        '.jpg': 'images',
        '.png': 'images',
        '.pdf': 'documents',
    }
    
    moves = []
    for file in path.iterdir():
        if file.is_file():
            ext = file.suffix.lower()
            folder_name = extension_map.get(ext, 'other')
            dest_folder = path / folder_name
            moves.append((file, dest_folder / file.name))
    
    if dry_run:
        print("Would move:")
        for src, dst in moves:
            print(f"  {src.name} -> {dst.parent.name}/{dst.name}")
    else:
        for src, dst in moves:
            dst.parent.mkdir(exist_ok=True)
            src.rename(dst)
    
    return moves

# Demo
with tempfile.TemporaryDirectory() as tmpdir:
    tmp = Path(tmpdir)
    (tmp / 'script.py').write_text('# python')
    (tmp / 'notes.txt').write_text('notes')
    (tmp / 'data.csv').write_text('a,b,c')
    
    organize_by_extension(tmpdir)

# SOLUTION 6
print("\n--- Solution 6: Path Statistics ---")

def get_path_stats(path):
    """Get statistics about a directory."""
    path = Path(path)
    
    stats = {
        'total_files': 0,
        'total_dirs': 0,
        'total_size': 0,
        'file_types': {},
    }
    
    for item in path.rglob('*'):
        if item.is_file():
            stats['total_files'] += 1
            stats['total_size'] += item.stat().st_size
            ext = item.suffix.lower() or 'no extension'
            stats['file_types'][ext] = stats['file_types'].get(ext, 0) + 1
        elif item.is_dir():
            stats['total_dirs'] += 1
    
    return stats

# Demo
with tempfile.TemporaryDirectory() as tmpdir:
    tmp = Path(tmpdir)
    (tmp / 'file1.py').write_text('# python')
    (tmp / 'file2.py').write_text('# python')
    (tmp / 'data.txt').write_text('data')
    (tmp / 'subdir').mkdir()
    (tmp / 'subdir' / 'nested.txt').write_text('nested')
    
    stats = get_path_stats(tmpdir)
    print("Directory statistics:")
    for key, value in stats.items():
        print(f"  {key}: {value}")

# SOLUTION 8
print("\n--- Solution 8: Log Parser ---")

from collections import Counter
import re

def parse_logs(log_content):
    """Parse log content and extract statistics."""
    result = {
        'error_counts': Counter(),
        'common_errors': [],
        'first_timestamp': None,
        'last_timestamp': None,
    }
    
    # Pattern: 2024-01-01 10:00:00 - LEVEL - message
    pattern = r'(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) - (\w+) - (.+)'
    
    timestamps = []
    error_messages = []
    
    for line in log_content.strip().split('\n'):
        match = re.match(pattern, line)
        if match:
            timestamp, level, message = match.groups()
            timestamps.append(timestamp)
            
            if level == 'ERROR':
                hour = timestamp.split()[1].split(':')[0]
                result['error_counts'][hour] += 1
                error_messages.append(message)
    
    if timestamps:
        result['first_timestamp'] = timestamps[0]
        result['last_timestamp'] = timestamps[-1]
    
    result['common_errors'] = Counter(error_messages).most_common(3)
    
    return result

# Demo
sample_logs = """
2024-01-01 10:00:00 - INFO - Application started
2024-01-01 10:15:00 - ERROR - Database connection failed
2024-01-01 10:30:00 - ERROR - Database connection failed
2024-01-01 11:00:00 - INFO - Retry successful
2024-01-01 11:30:00 - ERROR - Timeout error
2024-01-01 12:00:00 - INFO - Shutdown complete
"""

result = parse_logs(sample_logs)
print("Log analysis:")
print(f"  First: {result['first_timestamp']}")
print(f"  Last: {result['last_timestamp']}")
print(f"  Errors by hour: {dict(result['error_counts'])}")
print(f"  Common errors: {result['common_errors']}")

print("\n" + "=" * 60)
print("END OF EXERCISES")
print("=" * 60)
Exercises - Python Tutorial | DeepML