fauna.http.httpx_client
1import json 2from contextlib import contextmanager 3from json import JSONDecodeError 4from typing import Mapping, Any, Optional, Iterator 5 6import httpx 7 8from fauna.errors import ClientError, NetworkError 9from fauna.http.http_client import HTTPResponse, HTTPClient 10 11 12class HTTPXResponse(HTTPResponse): 13 14 def __init__(self, response: httpx.Response): 15 self._r = response 16 17 def headers(self) -> Mapping[str, str]: 18 h = {} 19 for (k, v) in self._r.headers.items(): 20 h[k] = v 21 return h 22 23 def json(self) -> Any: 24 try: 25 decoded = self._r.read().decode("utf-8") 26 return json.loads(decoded) 27 except (JSONDecodeError, UnicodeDecodeError) as e: 28 raise ClientError( 29 f"Unable to decode response from endpoint {self._r.request.url}. Check that your endpoint is valid." 30 ) from e 31 32 def text(self) -> str: 33 return str(self.read(), encoding='utf-8') 34 35 def status_code(self) -> int: 36 return self._r.status_code 37 38 def read(self) -> bytes: 39 return self._r.read() 40 41 def iter_bytes(self, size: Optional[int] = None) -> Iterator[bytes]: 42 return self._r.iter_bytes(size) 43 44 def close(self) -> None: 45 try: 46 self._r.close() 47 except Exception as e: 48 raise ClientError("Error closing response") from e 49 50 51class HTTPXClient(HTTPClient): 52 53 def __init__(self, client: httpx.Client): 54 super(HTTPXClient, self).__init__() 55 self._c = client 56 57 def request( 58 self, 59 method: str, 60 url: str, 61 headers: Mapping[str, str], 62 data: Mapping[str, Any], 63 ) -> HTTPResponse: 64 65 try: 66 request = self._c.build_request( 67 method, 68 url, 69 json=data, 70 headers=headers, 71 ) 72 except httpx.InvalidURL as e: 73 raise ClientError("Invalid URL Format") from e 74 75 try: 76 return HTTPXResponse(self._c.send( 77 request, 78 stream=False, 79 )) 80 except (httpx.HTTPError, httpx.InvalidURL) as e: 81 raise NetworkError("Exception re-raised from HTTP request") from e 82 83 @contextmanager 84 def stream( 85 self, 86 url: str, 87 headers: Mapping[str, str], 88 data: Mapping[str, Any], 89 ) -> Iterator[Any]: 90 with self._c.stream( 91 "POST", url=url, headers=headers, json=data) as response: 92 yield self._transform(response) 93 94 def _transform(self, response): 95 try: 96 for line in response.iter_lines(): 97 yield json.loads(line) 98 except httpx.ReadTimeout as e: 99 raise NetworkError("Stream timeout") from e 100 except (httpx.HTTPError, httpx.InvalidURL) as e: 101 raise NetworkError("Exception re-raised from HTTP request") from e 102 103 def close(self): 104 self._c.close()
13class HTTPXResponse(HTTPResponse): 14 15 def __init__(self, response: httpx.Response): 16 self._r = response 17 18 def headers(self) -> Mapping[str, str]: 19 h = {} 20 for (k, v) in self._r.headers.items(): 21 h[k] = v 22 return h 23 24 def json(self) -> Any: 25 try: 26 decoded = self._r.read().decode("utf-8") 27 return json.loads(decoded) 28 except (JSONDecodeError, UnicodeDecodeError) as e: 29 raise ClientError( 30 f"Unable to decode response from endpoint {self._r.request.url}. Check that your endpoint is valid." 31 ) from e 32 33 def text(self) -> str: 34 return str(self.read(), encoding='utf-8') 35 36 def status_code(self) -> int: 37 return self._r.status_code 38 39 def read(self) -> bytes: 40 return self._r.read() 41 42 def iter_bytes(self, size: Optional[int] = None) -> Iterator[bytes]: 43 return self._r.iter_bytes(size) 44 45 def close(self) -> None: 46 try: 47 self._r.close() 48 except Exception as e: 49 raise ClientError("Error closing response") from e
Helper class that provides a standard way to create an ABC using inheritance.
52class HTTPXClient(HTTPClient): 53 54 def __init__(self, client: httpx.Client): 55 super(HTTPXClient, self).__init__() 56 self._c = client 57 58 def request( 59 self, 60 method: str, 61 url: str, 62 headers: Mapping[str, str], 63 data: Mapping[str, Any], 64 ) -> HTTPResponse: 65 66 try: 67 request = self._c.build_request( 68 method, 69 url, 70 json=data, 71 headers=headers, 72 ) 73 except httpx.InvalidURL as e: 74 raise ClientError("Invalid URL Format") from e 75 76 try: 77 return HTTPXResponse(self._c.send( 78 request, 79 stream=False, 80 )) 81 except (httpx.HTTPError, httpx.InvalidURL) as e: 82 raise NetworkError("Exception re-raised from HTTP request") from e 83 84 @contextmanager 85 def stream( 86 self, 87 url: str, 88 headers: Mapping[str, str], 89 data: Mapping[str, Any], 90 ) -> Iterator[Any]: 91 with self._c.stream( 92 "POST", url=url, headers=headers, json=data) as response: 93 yield self._transform(response) 94 95 def _transform(self, response): 96 try: 97 for line in response.iter_lines(): 98 yield json.loads(line) 99 except httpx.ReadTimeout as e: 100 raise NetworkError("Stream timeout") from e 101 except (httpx.HTTPError, httpx.InvalidURL) as e: 102 raise NetworkError("Exception re-raised from HTTP request") from e 103 104 def close(self): 105 self._c.close()
Helper class that provides a standard way to create an ABC using inheritance.
def
request( self, method: str, url: str, headers: Mapping[str, str], data: Mapping[str, Any]) -> fauna.http.http_client.HTTPResponse:
58 def request( 59 self, 60 method: str, 61 url: str, 62 headers: Mapping[str, str], 63 data: Mapping[str, Any], 64 ) -> HTTPResponse: 65 66 try: 67 request = self._c.build_request( 68 method, 69 url, 70 json=data, 71 headers=headers, 72 ) 73 except httpx.InvalidURL as e: 74 raise ClientError("Invalid URL Format") from e 75 76 try: 77 return HTTPXResponse(self._c.send( 78 request, 79 stream=False, 80 )) 81 except (httpx.HTTPError, httpx.InvalidURL) as e: 82 raise NetworkError("Exception re-raised from HTTP request") from e