| """ |
| Base API Client |
| Abstract base class for all API clients |
| """ |
|
|
| from abc import ABC, abstractmethod |
| import requests |
| from typing import Dict, Any, Optional |
| import time |
|
|
|
|
| class BaseAPIClient(ABC): |
| """ |
| Abstract base class for API clients |
| |
| Provides common functionality: |
| - HTTP request handling |
| - Error handling |
| - Rate limiting |
| - Response validation |
| """ |
| |
| def __init__(self): |
| """Initialize base client""" |
| self.session = requests.Session() |
| self.timeout = 30 |
| self.last_request_time = 0 |
| self.min_request_interval = 0.1 |
| |
| def _wait_for_rate_limit(self): |
| """Implement simple rate limiting""" |
| elapsed = time.time() - self.last_request_time |
| if elapsed < self.min_request_interval: |
| time.sleep(self.min_request_interval - elapsed) |
| self.last_request_time = time.time() |
| |
| def _make_request( |
| self, |
| method: str, |
| url: str, |
| params: Optional[Dict] = None, |
| headers: Optional[Dict] = None, |
| data: Optional[Dict] = None, |
| json: Optional[Dict] = None |
| ) -> requests.Response: |
| """ |
| Make HTTP request with error handling |
| |
| Args: |
| method: HTTP method (GET, POST, etc.) |
| url: Request URL |
| params: Query parameters |
| headers: Request headers |
| data: Form data |
| json: JSON data |
| |
| Returns: |
| Response object |
| |
| Raises: |
| requests.exceptions.RequestException: On request failure |
| """ |
| self._wait_for_rate_limit() |
| |
| try: |
| response = self.session.request( |
| method=method, |
| url=url, |
| params=params, |
| headers=headers, |
| data=data, |
| json=json, |
| timeout=self.timeout |
| ) |
| response.raise_for_status() |
| return response |
| |
| except requests.exceptions.Timeout: |
| raise requests.exceptions.RequestException( |
| f"Request to {url} timed out after {self.timeout}s" |
| ) |
| except requests.exceptions.ConnectionError: |
| raise requests.exceptions.RequestException( |
| f"Failed to connect to {url}. Check your internet connection." |
| ) |
| except requests.exceptions.HTTPError as e: |
| status_code = e.response.status_code |
| if status_code == 401: |
| raise requests.exceptions.RequestException( |
| "Authentication failed. Check your API key." |
| ) |
| elif status_code == 429: |
| raise requests.exceptions.RequestException( |
| "Rate limit exceeded. Please wait before making more requests." |
| ) |
| elif status_code == 404: |
| raise requests.exceptions.RequestException( |
| "Resource not found. Check your request parameters." |
| ) |
| else: |
| raise requests.exceptions.RequestException( |
| f"HTTP {status_code} error: {str(e)}" |
| ) |
| |
| def get(self, url: str, params: Optional[Dict] = None, |
| headers: Optional[Dict] = None) -> Dict[str, Any]: |
| """ |
| Make GET request |
| |
| Args: |
| url: Request URL |
| params: Query parameters |
| headers: Request headers |
| |
| Returns: |
| JSON response as dict |
| """ |
| response = self._make_request("GET", url, params=params, headers=headers) |
| return response.json() |
| |
| def post(self, url: str, data: Optional[Dict] = None, |
| json: Optional[Dict] = None, headers: Optional[Dict] = None) -> Dict[str, Any]: |
| """ |
| Make POST request |
| |
| Args: |
| url: Request URL |
| data: Form data |
| json: JSON data |
| headers: Request headers |
| |
| Returns: |
| JSON response as dict |
| """ |
| response = self._make_request("POST", url, data=data, json=json, headers=headers) |
| return response.json() |
| |
| @abstractmethod |
| def _validate_params(self, **kwargs) -> bool: |
| """ |
| Validate request parameters |
| Should be implemented by subclasses |
| |
| Args: |
| **kwargs: Parameters to validate |
| |
| Returns: |
| True if valid |
| |
| Raises: |
| ValueError: If parameters are invalid |
| """ |
| pass |
| |
| def __del__(self): |
| """Cleanup: close session""" |
| if hasattr(self, 'session'): |
| self.session.close() |
|
|
|
|