70 lines
2.4 KiB
Python
70 lines
2.4 KiB
Python
"""
|
|
fetchers/base.py — Abstract base class for all macro data fetchers.
|
|
|
|
Provides retry logic, rate limiting, and a common interface.
|
|
"""
|
|
|
|
from abc import ABC, abstractmethod
|
|
from datetime import date as Date
|
|
from typing import Optional
|
|
import logging
|
|
import time
|
|
import requests
|
|
|
|
|
|
class BaseFetcher(ABC):
|
|
"""Abstract base for all macro data fetchers."""
|
|
|
|
def __init__(self, timeout: int = 30, max_retries: int = 3):
|
|
self.timeout = timeout
|
|
self.max_retries = max_retries
|
|
self.logger = logging.getLogger(self.__class__.__name__)
|
|
|
|
def _get(self, url: str, params: Optional[dict] = None,
|
|
headers: Optional[dict] = None) -> dict:
|
|
"""GET with retry and exponential backoff."""
|
|
for attempt in range(self.max_retries):
|
|
try:
|
|
resp = requests.get(
|
|
url, params=params, headers=headers, timeout=self.timeout
|
|
)
|
|
resp.raise_for_status()
|
|
return resp.json()
|
|
except requests.RequestException as e:
|
|
wait = 2 ** attempt
|
|
self.logger.warning(
|
|
f"Request failed (attempt {attempt+1}/{self.max_retries}): {e}. "
|
|
f"Retrying in {wait}s"
|
|
)
|
|
if attempt < self.max_retries - 1:
|
|
time.sleep(wait)
|
|
else:
|
|
raise
|
|
|
|
def _get_raw(self, url: str, params: Optional[dict] = None,
|
|
headers: Optional[dict] = None) -> bytes:
|
|
"""GET raw bytes with retry (for non-JSON endpoints)."""
|
|
for attempt in range(self.max_retries):
|
|
try:
|
|
resp = requests.get(
|
|
url, params=params, headers=headers, timeout=self.timeout
|
|
)
|
|
resp.raise_for_status()
|
|
return resp.content
|
|
except requests.RequestException as e:
|
|
wait = 2 ** attempt
|
|
if attempt < self.max_retries - 1:
|
|
time.sleep(wait)
|
|
else:
|
|
raise
|
|
|
|
@abstractmethod
|
|
def fetch(self, target_date: Optional[Date] = None) -> list[dict]:
|
|
"""Fetch raw data. Returns list of record dicts."""
|
|
...
|
|
|
|
@abstractmethod
|
|
def store(self, db_path: str, records: list[dict]) -> int:
|
|
"""Store raw records into SQLite. Returns count of new rows."""
|
|
...
|