93 lines
2.6 KiB
Python
93 lines
2.6 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
|
|
"""
|
|
Shared Open-Meteo HTTP client with retries and sane timeouts.
|
|
Use for all calls to Open-Meteo APIs to reduce transient timeouts/502s.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Dict, Optional, Tuple
|
|
|
|
import requests
|
|
from requests.adapters import HTTPAdapter
|
|
from urllib3.util.retry import Retry
|
|
|
|
DEFAULT_TIMEOUT: Tuple[int, int] = (5, 25) # connect, read
|
|
|
|
_SESSION_CACHE: dict[tuple, requests.Session] = {}
|
|
|
|
|
|
def _session_key(headers: Optional[Dict[str, str]], retries: int, backoff: float) -> tuple:
|
|
headers_key = tuple(sorted(headers.items())) if headers else ()
|
|
return headers_key, retries, backoff
|
|
|
|
|
|
def get_open_meteo_session(
|
|
headers: Optional[Dict[str, str]] = None,
|
|
retries: int = 3,
|
|
backoff: float = 0.8,
|
|
) -> requests.Session:
|
|
key = _session_key(headers, retries, backoff)
|
|
if key in _SESSION_CACHE:
|
|
return _SESSION_CACHE[key]
|
|
|
|
retry = Retry(
|
|
total=retries,
|
|
connect=retries,
|
|
read=retries,
|
|
status=retries,
|
|
status_forcelist=(429, 500, 502, 503, 504),
|
|
allowed_methods=frozenset(["GET"]),
|
|
backoff_factor=backoff,
|
|
raise_on_status=False,
|
|
respect_retry_after_header=True,
|
|
)
|
|
adapter = HTTPAdapter(max_retries=retry)
|
|
session = requests.Session()
|
|
session.mount("https://", adapter)
|
|
session.mount("http://", adapter)
|
|
if headers:
|
|
session.headers.update(headers)
|
|
|
|
_SESSION_CACHE[key] = session
|
|
return session
|
|
|
|
|
|
def configure_open_meteo_session(
|
|
session: requests.Session,
|
|
headers: Optional[Dict[str, str]] = None,
|
|
retries: int = 3,
|
|
backoff: float = 0.8,
|
|
) -> requests.Session:
|
|
retry = Retry(
|
|
total=retries,
|
|
connect=retries,
|
|
read=retries,
|
|
status=retries,
|
|
status_forcelist=(429, 500, 502, 503, 504),
|
|
allowed_methods=frozenset(["GET"]),
|
|
backoff_factor=backoff,
|
|
raise_on_status=False,
|
|
respect_retry_after_header=True,
|
|
)
|
|
adapter = HTTPAdapter(max_retries=retry)
|
|
session.mount("https://", adapter)
|
|
session.mount("http://", adapter)
|
|
if headers:
|
|
session.headers.update(headers)
|
|
return session
|
|
|
|
|
|
def open_meteo_get(
|
|
url: str,
|
|
params: Optional[Dict] = None,
|
|
headers: Optional[Dict[str, str]] = None,
|
|
timeout: Tuple[int, int] = DEFAULT_TIMEOUT,
|
|
retries: int = 3,
|
|
backoff: float = 0.8,
|
|
) -> requests.Response:
|
|
session = get_open_meteo_session(headers=headers, retries=retries, backoff=backoff)
|
|
return session.get(url, params=params, timeout=timeout)
|