#!/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)