python

exercises

exercises.py🐍
"""
Python Web Development - Exercises
Practice building web applications and APIs
"""

from typing import Dict, List, Optional, Any, Callable
from dataclasses import dataclass, field
from datetime import datetime, timedelta
import json
import re
import hashlib
import secrets


# ============================================================
# EXERCISE 1: HTTP Request Parser
# ============================================================
"""
Parse raw HTTP request text into a structured object.
"""

@dataclass
class ParsedRequest:
    """Parsed HTTP request."""
    method: str
    path: str
    version: str
    headers: Dict[str, str]
    body: Optional[str]


def parse_http_request(raw_request: str) -> ParsedRequest:
    """
    Parse a raw HTTP request string.
    
    Example:
        >>> raw = '''GET /users HTTP/1.1
        ... Host: example.com
        ... Content-Type: application/json
        ... 
        ... {"name": "Alice"}'''
        >>> req = parse_http_request(raw)
        >>> req.method
        'GET'
        >>> req.path
        '/users'
        >>> req.headers['Host']
        'example.com'
    """
    # YOUR CODE HERE
    pass


# ============================================================
# EXERCISE 2: URL Router with Parameters
# ============================================================
"""
Build a URL router that supports path parameters like /users/:id
"""

class Router:
    """
    URL router with path parameter support.
    
    Example:
        >>> router = Router()
        >>> @router.get("/users/:user_id")
        ... def get_user(user_id):
        ...     return {"user_id": user_id}
        >>> 
        >>> handler, params = router.match("GET", "/users/123")
        >>> handler(**params)
        {'user_id': '123'}
    """
    
    def __init__(self):
        self.routes: List[tuple] = []  # (method, pattern, handler)
    
    def add_route(self, method: str, path: str, handler: Callable):
        """Add a route with optional path parameters."""
        # YOUR CODE HERE
        pass
    
    def get(self, path: str):
        """Decorator for GET routes."""
        # YOUR CODE HERE
        pass
    
    def post(self, path: str):
        """Decorator for POST routes."""
        # YOUR CODE HERE
        pass
    
    def match(self, method: str, path: str) -> tuple:
        """
        Match a request to a handler.
        Returns (handler, params) or (None, None) if no match.
        """
        # YOUR CODE HERE
        pass


# ============================================================
# EXERCISE 3: Request Validator
# ============================================================
"""
Create a validator for API request bodies with various rules.
"""

class Field:
    """Field definition with validation rules."""
    
    def __init__(
        self,
        field_type: type = str,
        required: bool = True,
        min_length: Optional[int] = None,
        max_length: Optional[int] = None,
        min_value: Optional[float] = None,
        max_value: Optional[float] = None,
        pattern: Optional[str] = None,
        choices: Optional[List] = None,
        default: Any = None
    ):
        self.field_type = field_type
        self.required = required
        self.min_length = min_length
        self.max_length = max_length
        self.min_value = min_value
        self.max_value = max_value
        self.pattern = pattern
        self.choices = choices
        self.default = default


class RequestValidator:
    """
    Validate request data against a schema.
    
    Example:
        >>> validator = RequestValidator({
        ...     'username': Field(str, min_length=3, max_length=20),
        ...     'email': Field(str, pattern=r'^[^@]+@[^@]+[.][^@]+$'),
        ...     'age': Field(int, required=False, min_value=0, max_value=150),
        ...     'role': Field(str, choices=['admin', 'user', 'guest'])
        ... })
        >>> 
        >>> data = {'username': 'alice', 'email': 'alice@test.com', 'role': 'user'}
        >>> validated = validator.validate(data)
    """
    
    def __init__(self, schema: Dict[str, Field]):
        self.schema = schema
    
    def validate(self, data: Dict) -> Dict:
        """
        Validate data against schema.
        Returns validated data or raises ValidationError.
        """
        # YOUR CODE HERE
        pass


class ValidationError(Exception):
    """Validation error with field-level errors."""
    
    def __init__(self, errors: Dict[str, List[str]]):
        self.errors = errors
        super().__init__(str(errors))


# ============================================================
# EXERCISE 4: Session Manager
# ============================================================
"""
Implement server-side session management.
"""

@dataclass
class Session:
    """Session data container."""
    id: str
    data: Dict[str, Any]
    created_at: datetime
    last_accessed: datetime
    expires_at: datetime


class SessionManager:
    """
    Server-side session management.
    
    Example:
        >>> manager = SessionManager(max_age=3600)
        >>> session_id = manager.create()
        >>> manager.set(session_id, 'user_id', 123)
        >>> manager.get(session_id, 'user_id')
        123
        >>> manager.destroy(session_id)
    """
    
    def __init__(self, max_age: int = 3600):
        self.max_age = max_age
        self.sessions: Dict[str, Session] = {}
    
    def create(self) -> str:
        """Create a new session and return its ID."""
        # YOUR CODE HERE
        pass
    
    def get(self, session_id: str, key: str, default: Any = None) -> Any:
        """Get a value from session."""
        # YOUR CODE HERE
        pass
    
    def set(self, session_id: str, key: str, value: Any):
        """Set a value in session."""
        # YOUR CODE HERE
        pass
    
    def destroy(self, session_id: str):
        """Destroy a session."""
        # YOUR CODE HERE
        pass
    
    def cleanup_expired(self) -> int:
        """Remove expired sessions, return count removed."""
        # YOUR CODE HERE
        pass


# ============================================================
# EXERCISE 5: Rate Limiter Middleware
# ============================================================
"""
Implement a rate limiter for API endpoints.
"""

class RateLimiter:
    """
    Token bucket rate limiter.
    
    Example:
        >>> limiter = RateLimiter(max_requests=10, window_seconds=60)
        >>> limiter.is_allowed("user_123")  # True
        >>> # ... 10 more requests ...
        >>> limiter.is_allowed("user_123")  # False (rate limited)
    """
    
    def __init__(self, max_requests: int = 100, window_seconds: int = 60):
        self.max_requests = max_requests
        self.window_seconds = window_seconds
        self.requests: Dict[str, List[datetime]] = {}
    
    def is_allowed(self, key: str) -> bool:
        """Check if request is allowed for given key."""
        # YOUR CODE HERE
        pass
    
    def get_remaining(self, key: str) -> int:
        """Get remaining requests for key."""
        # YOUR CODE HERE
        pass
    
    def get_reset_time(self, key: str) -> Optional[datetime]:
        """Get when the rate limit resets for key."""
        # YOUR CODE HERE
        pass


def rate_limit(limiter: RateLimiter, key_func: Callable):
    """
    Rate limit decorator.
    
    Example:
        >>> @rate_limit(limiter, lambda req: req.headers.get('X-API-Key'))
        ... def api_handler(request):
        ...     return {"data": "..."}
    """
    # YOUR CODE HERE
    pass


# ============================================================
# EXERCISE 6: JWT Authentication
# ============================================================
"""
Implement JWT token creation and verification.
"""

class JWTAuth:
    """
    JWT authentication handler.
    
    Example:
        >>> auth = JWTAuth(secret="my-secret-key")
        >>> token = auth.create_token({"user_id": 123, "role": "admin"})
        >>> payload = auth.verify_token(token)
        >>> payload['user_id']
        123
    """
    
    def __init__(self, secret: str, algorithm: str = "HS256"):
        self.secret = secret
        self.algorithm = algorithm
    
    def create_token(
        self,
        payload: Dict[str, Any],
        expires_in: int = 3600
    ) -> str:
        """Create a JWT token."""
        # YOUR CODE HERE
        # Hint: Use base64 encoding and HMAC for signature
        pass
    
    def verify_token(self, token: str) -> Optional[Dict[str, Any]]:
        """Verify and decode a JWT token."""
        # YOUR CODE HERE
        pass
    
    def refresh_token(self, token: str, expires_in: int = 3600) -> Optional[str]:
        """Refresh an existing token with new expiration."""
        # YOUR CODE HERE
        pass


# ============================================================
# EXERCISE 7: REST Resource Handler
# ============================================================
"""
Create a generic REST resource handler with CRUD operations.
"""

class RESTResource:
    """
    Generic REST resource with CRUD operations.
    
    Example:
        >>> users = RESTResource("users")
        >>> user = users.create({"name": "Alice", "email": "alice@test.com"})
        >>> users.read(user['id'])
        {'id': 1, 'name': 'Alice', 'email': 'alice@test.com', ...}
        >>> users.update(1, {"name": "Alice Smith"})
        >>> users.delete(1)
    """
    
    def __init__(self, name: str, validator: Optional[RequestValidator] = None):
        self.name = name
        self.validator = validator
        self.items: Dict[int, Dict] = {}
        self.counter = 0
    
    def list(
        self,
        page: int = 1,
        per_page: int = 10,
        filters: Optional[Dict] = None,
        sort_by: Optional[str] = None,
        sort_order: str = "asc"
    ) -> Dict:
        """
        List resources with pagination, filtering, and sorting.
        """
        # YOUR CODE HERE
        pass
    
    def create(self, data: Dict) -> Dict:
        """Create a new resource."""
        # YOUR CODE HERE
        pass
    
    def read(self, resource_id: int) -> Optional[Dict]:
        """Read a single resource."""
        # YOUR CODE HERE
        pass
    
    def update(self, resource_id: int, data: Dict, partial: bool = False) -> Optional[Dict]:
        """Update a resource (full or partial)."""
        # YOUR CODE HERE
        pass
    
    def delete(self, resource_id: int) -> bool:
        """Delete a resource."""
        # YOUR CODE HERE
        pass


# ============================================================
# EXERCISE 8: Middleware Chain
# ============================================================
"""
Implement a middleware chain for request processing.
"""

@dataclass
class Request:
    """HTTP request object."""
    method: str = "GET"
    path: str = "/"
    headers: Dict[str, str] = field(default_factory=dict)
    body: Optional[Dict] = None
    context: Dict[str, Any] = field(default_factory=dict)


@dataclass
class Response:
    """HTTP response object."""
    status_code: int = 200
    headers: Dict[str, str] = field(default_factory=dict)
    body: Any = None


class MiddlewareStack:
    """
    Middleware stack for request/response processing.
    
    Example:
        >>> stack = MiddlewareStack()
        >>> stack.use(logging_middleware)
        >>> stack.use(auth_middleware)
        >>> stack.use(timing_middleware)
        >>> 
        >>> response = stack.handle(request, final_handler)
    """
    
    def __init__(self):
        self.middlewares: List[Callable] = []
    
    def use(self, middleware: Callable):
        """Add middleware to stack."""
        # YOUR CODE HERE
        pass
    
    def handle(self, request: Request, handler: Callable) -> Response:
        """Process request through middleware stack."""
        # YOUR CODE HERE
        pass


# Example middleware to implement
def create_logging_middleware():
    """Create logging middleware."""
    # YOUR CODE HERE
    pass

def create_auth_middleware(token_validator: Callable):
    """Create authentication middleware."""
    # YOUR CODE HERE
    pass

def create_cors_middleware(allowed_origins: List[str]):
    """Create CORS middleware."""
    # YOUR CODE HERE
    pass


# ============================================================
# EXERCISE 9: WebSocket Handler (Simulated)
# ============================================================
"""
Simulate WebSocket connection handling.
"""

class WebSocketConnection:
    """Simulated WebSocket connection."""
    
    def __init__(self, id: str):
        self.id = id
        self.messages: List[str] = []
        self.is_open = True
    
    def send(self, message: str):
        """Send message to client."""
        if self.is_open:
            self.messages.append(f"SENT: {message}")
    
    def receive(self) -> Optional[str]:
        """Receive message from client (simulated)."""
        return None
    
    def close(self):
        """Close connection."""
        self.is_open = False


class WebSocketHub:
    """
    WebSocket connection hub for managing multiple connections.
    
    Example:
        >>> hub = WebSocketHub()
        >>> conn1 = hub.connect("user_1")
        >>> conn2 = hub.connect("user_2")
        >>> hub.broadcast("Hello everyone!")
        >>> hub.send_to("user_1", "Private message")
        >>> hub.join_room("user_1", "chat_room_1")
        >>> hub.broadcast_to_room("chat_room_1", "Room message")
    """
    
    def __init__(self):
        self.connections: Dict[str, WebSocketConnection] = {}
        self.rooms: Dict[str, set] = {}
    
    def connect(self, user_id: str) -> WebSocketConnection:
        """Create new connection for user."""
        # YOUR CODE HERE
        pass
    
    def disconnect(self, user_id: str):
        """Disconnect user."""
        # YOUR CODE HERE
        pass
    
    def send_to(self, user_id: str, message: str):
        """Send message to specific user."""
        # YOUR CODE HERE
        pass
    
    def broadcast(self, message: str, exclude: Optional[List[str]] = None):
        """Broadcast message to all connected users."""
        # YOUR CODE HERE
        pass
    
    def join_room(self, user_id: str, room: str):
        """Add user to a room."""
        # YOUR CODE HERE
        pass
    
    def leave_room(self, user_id: str, room: str):
        """Remove user from a room."""
        # YOUR CODE HERE
        pass
    
    def broadcast_to_room(self, room: str, message: str, exclude: Optional[List[str]] = None):
        """Broadcast message to all users in a room."""
        # YOUR CODE HERE
        pass


# ============================================================
# EXERCISE 10: API Application
# ============================================================
"""
Build a complete mini API application combining all concepts.
"""

class MiniAPI:
    """
    Mini API framework combining router, middleware, and resources.
    
    Example:
        >>> api = MiniAPI()
        >>> api.use(logging_middleware)
        >>> 
        >>> users = api.resource("/users", validator=user_validator)
        >>> 
        >>> @api.get("/health")
        ... def health_check():
        ...     return {"status": "ok"}
        >>> 
        >>> response = api.handle(Request(method="GET", path="/users"))
    """
    
    def __init__(self):
        self.router = Router()
        self.middlewares: List[Callable] = []
        self.resources: Dict[str, RESTResource] = {}
    
    def use(self, middleware: Callable):
        """Add middleware."""
        # YOUR CODE HERE
        pass
    
    def get(self, path: str):
        """Register GET route."""
        # YOUR CODE HERE
        pass
    
    def post(self, path: str):
        """Register POST route."""
        # YOUR CODE HERE
        pass
    
    def resource(self, path: str, validator: Optional[RequestValidator] = None) -> RESTResource:
        """Register a REST resource."""
        # YOUR CODE HERE
        pass
    
    def handle(self, request: Request) -> Response:
        """Handle incoming request."""
        # YOUR CODE HERE
        pass


# ============================================================
# SOLUTIONS (Uncomment to check your work)
# ============================================================

"""
# Solution 4: Session Manager
class SessionManager:
    def __init__(self, max_age: int = 3600):
        self.max_age = max_age
        self.sessions: Dict[str, Session] = {}
    
    def create(self) -> str:
        session_id = secrets.token_urlsafe(32)
        now = datetime.now()
        self.sessions[session_id] = Session(
            id=session_id,
            data={},
            created_at=now,
            last_accessed=now,
            expires_at=now + timedelta(seconds=self.max_age)
        )
        return session_id
    
    def get(self, session_id: str, key: str, default: Any = None) -> Any:
        session = self.sessions.get(session_id)
        if not session or datetime.now() > session.expires_at:
            return default
        session.last_accessed = datetime.now()
        return session.data.get(key, default)
    
    def set(self, session_id: str, key: str, value: Any):
        session = self.sessions.get(session_id)
        if session and datetime.now() <= session.expires_at:
            session.data[key] = value
            session.last_accessed = datetime.now()
    
    def destroy(self, session_id: str):
        self.sessions.pop(session_id, None)
    
    def cleanup_expired(self) -> int:
        now = datetime.now()
        expired = [sid for sid, s in self.sessions.items() if now > s.expires_at]
        for sid in expired:
            del self.sessions[sid]
        return len(expired)


# Solution 5: Rate Limiter
class RateLimiter:
    def __init__(self, max_requests: int = 100, window_seconds: int = 60):
        self.max_requests = max_requests
        self.window_seconds = window_seconds
        self.requests: Dict[str, List[datetime]] = {}
    
    def is_allowed(self, key: str) -> bool:
        now = datetime.now()
        window_start = now - timedelta(seconds=self.window_seconds)
        
        if key not in self.requests:
            self.requests[key] = []
        
        # Clean old requests
        self.requests[key] = [t for t in self.requests[key] if t > window_start]
        
        if len(self.requests[key]) < self.max_requests:
            self.requests[key].append(now)
            return True
        return False
    
    def get_remaining(self, key: str) -> int:
        now = datetime.now()
        window_start = now - timedelta(seconds=self.window_seconds)
        
        if key not in self.requests:
            return self.max_requests
        
        valid = [t for t in self.requests[key] if t > window_start]
        return max(0, self.max_requests - len(valid))


# Solution 7: REST Resource
class RESTResource:
    def __init__(self, name: str, validator: Optional[RequestValidator] = None):
        self.name = name
        self.validator = validator
        self.items: Dict[int, Dict] = {}
        self.counter = 0
    
    def list(self, page: int = 1, per_page: int = 10, filters: Optional[Dict] = None,
             sort_by: Optional[str] = None, sort_order: str = "asc") -> Dict:
        items = list(self.items.values())
        
        # Filter
        if filters:
            for key, value in filters.items():
                items = [i for i in items if i.get(key) == value]
        
        # Sort
        if sort_by:
            items.sort(key=lambda x: x.get(sort_by, ''), reverse=(sort_order == "desc"))
        
        # Paginate
        total = len(items)
        start = (page - 1) * per_page
        items = items[start:start + per_page]
        
        return {"items": items, "total": total, "page": page, "per_page": per_page}
    
    def create(self, data: Dict) -> Dict:
        if self.validator:
            data = self.validator.validate(data)
        
        self.counter += 1
        now = datetime.now().isoformat()
        item = {"id": self.counter, **data, "created_at": now, "updated_at": now}
        self.items[self.counter] = item
        return item
    
    def read(self, resource_id: int) -> Optional[Dict]:
        return self.items.get(resource_id)
    
    def update(self, resource_id: int, data: Dict, partial: bool = False) -> Optional[Dict]:
        if resource_id not in self.items:
            return None
        
        if partial:
            self.items[resource_id].update(data)
        else:
            self.items[resource_id] = {"id": resource_id, **data}
        
        self.items[resource_id]["updated_at"] = datetime.now().isoformat()
        return self.items[resource_id]
    
    def delete(self, resource_id: int) -> bool:
        if resource_id in self.items:
            del self.items[resource_id]
            return True
        return False
"""


if __name__ == "__main__":
    print("Web Development Exercises")
    print("=" * 50)
    print("\nComplete the exercises above to practice:")
    print("- HTTP request/response handling")
    print("- URL routing with parameters")
    print("- Request validation")
    print("- Session management")
    print("- Rate limiting")
    print("- JWT authentication")
    print("- REST API design")
    print("- Middleware patterns")
    print("- WebSocket handling")
    print("- Building complete APIs")
    print("\nUncomment the solutions to check your work!")
Exercises - Python Tutorial | DeepML