Spaces:
Sleeping
Sleeping
import json | |
import socket | |
import time | |
from typing import Optional, Any, Mapping, Callable, Type, Tuple | |
import requests | |
from requests import HTTPError | |
from urlobject import URLObject | |
from urlobject.path import URLPath | |
from .common import translate_dict_func | |
def get_host_ip() -> Optional[str]: | |
s = None | |
try: | |
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) | |
s.connect(('8.8.8.8', 80)) | |
ip = s.getsockname()[0] | |
finally: | |
if s is not None: | |
s.close() | |
return ip | |
_DEFAULT_HTTP_PORT = 80 | |
_DEFAULT_HTTPS_PORT = 443 | |
def split_http_address(address: str, default_port: Optional[int] = None) -> Tuple[str, int, bool, str]: | |
_url = URLObject(address) | |
_host = _url.hostname | |
_https = (_url.scheme.lower()) == 'https' | |
_port = _url.port or default_port or (_DEFAULT_HTTPS_PORT if _https else _DEFAULT_HTTP_PORT) | |
_path = str(_url.path) or '' | |
return _host, _port, _https, _path | |
DEFAULT_RETRIES = 5 | |
DEFAULT_RETRY_WAITING = 1.0 | |
class HttpEngine: | |
def __init__(self, host: str, port: int, https: bool = False, path: str = None): | |
self.__base_url = URLObject().with_scheme('https' if https else 'http') \ | |
.with_hostname(host).with_port(port).add_path(path or '') | |
self.__session = requests.session() | |
self.__session.trust_env = False | |
# noinspection PyMethodMayBeStatic | |
def _data_process(self, data: Optional[Mapping[str, Any]] = None) -> Mapping[str, Any]: | |
return data or {} | |
# noinspection PyMethodMayBeStatic | |
def _base_headers(self) -> Mapping[str, None]: | |
return {} | |
def _error_handler(self, err: Exception): | |
raise err | |
def get_url(self, path: str = None): | |
original_segments = self.__base_url.path.segments | |
path_segments = URLPath().add(path or '').segments | |
return str(self.__base_url.with_path(URLPath.join_segments(original_segments + path_segments))) | |
def __single_request( | |
self, | |
method: str, | |
path: str, | |
data: Optional[Mapping[str, Any]] = None, | |
headers: Optional[Mapping[str, Any]] = None, | |
params: Optional[Mapping[str, Any]] = None, | |
raise_for_status: bool = True | |
): | |
response = self.__session.request( | |
method=method, | |
url=self.get_url(path), | |
data=json.dumps(self._data_process(data) or {}), | |
headers=headers, | |
params=params or {}, | |
) | |
if raise_for_status: | |
response.raise_for_status() | |
return response | |
def request( | |
self, | |
method: str, | |
path: str, | |
data: Optional[Mapping[str, Any]] = None, | |
headers: Optional[Mapping[str, Any]] = None, | |
params: Optional[Mapping[str, Any]] = None, | |
raise_for_status: bool = True, | |
retries: Optional[int] = None, | |
retry_waiting: Optional[float] = None, | |
) -> requests.Response: | |
_headers = dict(self._base_headers()) | |
_headers.update(headers or {}) | |
retries = retries or DEFAULT_RETRIES | |
retry_waiting = retry_waiting or DEFAULT_RETRY_WAITING | |
try: | |
_current_retries = 0 | |
while True: | |
try: | |
response = self.__single_request(method, path, data, _headers, params, raise_for_status) | |
except requests.exceptions.HTTPError as err: | |
raise err | |
except requests.exceptions.RequestException as err: | |
_current_retries += 1 | |
if _current_retries > retries: | |
raise err | |
else: | |
time.sleep(retry_waiting) | |
else: | |
break | |
except Exception as e: | |
self._error_handler(e) | |
else: | |
return response | |
def get_http_engine_class( | |
headers: Mapping[str, Callable[..., Any]], | |
data_processor: Optional[Callable[[Mapping[str, Any]], Mapping[str, Any]]] = None, | |
http_error_gene: Optional[Callable[[HTTPError], Exception]] = None, | |
) -> Callable[..., Type[HttpEngine]]: | |
def _func(*args, **kwargs) -> Type[HttpEngine]: | |
class _HttpEngine(HttpEngine): | |
def _data_process(self, data: Optional[Mapping[str, Any]] = None) -> Mapping[str, Any]: | |
return (data_processor or (lambda d: d or {}))(data or {}) | |
def _base_headers(self) -> Mapping[str, None]: | |
return translate_dict_func(headers)(*args, **kwargs) | |
def _error_handler(self, err: Exception): | |
if http_error_gene is not None and isinstance(err, HTTPError): | |
raise http_error_gene(err) | |
else: | |
raise err | |
return _HttpEngine | |
return _func | |