Spaces:
Running
Running
| import time | |
| import json | |
| import logging | |
| import requests | |
| from datetime import datetime | |
| from functools import wraps | |
| from typing import Dict, Any | |
| logger = logging.getLogger(__name__) | |
| def with_error_handling(func): | |
| """Decorator to add comprehensive error handling.""" | |
| def wrapper(*args, **kwargs): | |
| start_time = time.time() | |
| try: | |
| safe_args = [] | |
| for i, arg in enumerate(args[:2]): | |
| if isinstance(arg, (str, int, float, bool)): | |
| safe_args.append(str(arg)) | |
| else: | |
| safe_args.append(f"<{type(arg).__name__}>") | |
| logger.info(f"Starting {func.__name__} with args: {safe_args}") | |
| result = func(*args, **kwargs) | |
| if isinstance(result, dict) and 'error' not in result: | |
| result = standardize_response(result, func.__name__) | |
| execution_time = time.time() - start_time | |
| logger.info(f"Completed {func.__name__} in {execution_time:.2f}s") | |
| return result | |
| except requests.exceptions.Timeout: | |
| logger.error(f"Timeout in {func.__name__}") | |
| return create_error_response("Request timeout", func.__name__) | |
| except requests.exceptions.ConnectionError: | |
| logger.error(f"Connection error in {func.__name__}") | |
| return create_error_response("Connection failed", func.__name__) | |
| except requests.exceptions.RequestException as e: | |
| logger.error(f"Request error in {func.__name__}: {e}") | |
| return create_error_response(f"Request failed: {str(e)}", func.__name__) | |
| except Exception as e: | |
| logger.error(f"Unexpected error in {func.__name__}: {e}") | |
| return create_error_response(f"Unexpected error: {str(e)}", func.__name__) | |
| return wrapper | |
| def standardize_response(data: Dict[str, Any], source: str) -> Dict[str, Any]: | |
| """Standardize API response format with metadata.""" | |
| return { | |
| "data": data, | |
| "metadata": { | |
| "source": source, | |
| "timestamp": datetime.now().isoformat(), | |
| "version": "1.1.0", | |
| "cached": False | |
| }, | |
| "status": "success" | |
| } | |
| def create_error_response(error_msg: str, source: str) -> Dict[str, Any]: | |
| """Create standardized error response.""" | |
| return { | |
| "data": None, | |
| "metadata": { | |
| "source": source, | |
| "timestamp": datetime.now().isoformat(), | |
| "version": "1.1.0" | |
| }, | |
| "status": "error", | |
| "error": error_msg | |
| } | |
| def make_api_request(url: str, params: Dict[str, Any], timeout: int = 15, max_retries: int = 3) -> requests.Response: | |
| """Make API request with retry logic and rate limiting.""" | |
| for attempt in range(max_retries): | |
| try: | |
| time.sleep(0.1 * attempt) | |
| response = requests.get(url, params=params, timeout=timeout) | |
| if response.status_code == 429: | |
| wait_time = 2 ** attempt | |
| logger.warning(f"Rate limited, waiting {wait_time}s before retry {attempt + 1}") | |
| time.sleep(wait_time) | |
| continue | |
| return response | |
| except requests.exceptions.RequestException as e: | |
| if attempt == max_retries - 1: | |
| raise | |
| logger.warning(f"Request attempt {attempt + 1} failed: {e}") | |
| raise requests.exceptions.RequestException("Max retries exceeded") | |
| def format_json_output(obj: Any) -> str: | |
| """ | |
| Formats a Python object as a pretty-printed JSON string. | |
| Args: | |
| obj: The Python object to format. | |
| Returns: | |
| A JSON string with an indent of 2 and UTF-8 characters preserved. | |
| """ | |
| return json.dumps(obj, indent=2, ensure_ascii=False) |