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()
class HTTPXResponse(fauna.http.http_client.HTTPResponse):
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.

HTTPXResponse(response: httpx.Response)
15  def __init__(self, response: httpx.Response):
16    self._r = response
def headers(self) -> Mapping[str, str]:
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
def json(self) -> Any:
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
def text(self) -> str:
33  def text(self) -> str:
34    return str(self.read(), encoding='utf-8')
def status_code(self) -> int:
36  def status_code(self) -> int:
37    return self._r.status_code
def read(self) -> bytes:
39  def read(self) -> bytes:
40    return self._r.read()
def iter_bytes(self, size: Optional[int] = None) -> Iterator[bytes]:
42  def iter_bytes(self, size: Optional[int] = None) -> Iterator[bytes]:
43    return self._r.iter_bytes(size)
def close(self) -> None:
45  def close(self) -> None:
46    try:
47      self._r.close()
48    except Exception as e:
49      raise ClientError("Error closing response") from e
class HTTPXClient(fauna.http.http_client.HTTPClient):
 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.

HTTPXClient(client: httpx.Client)
54  def __init__(self, client: httpx.Client):
55    super(HTTPXClient, self).__init__()
56    self._c = client
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
@contextmanager
def stream( self, url: str, headers: Mapping[str, str], data: Mapping[str, Any]) -> Iterator[Any]:
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)
def close(self):
104  def close(self):
105    self._c.close()