|
1 | 1 | from collections.abc import Mapping |
| 2 | +from json import JSONDecodeError |
2 | 3 | from typing import Any, Dict, Optional |
3 | 4 | from importlib.metadata import version |
4 | 5 | import abc |
|
14 | 15 | SeamHttpUnauthorizedError, |
15 | 16 | ) |
16 | 17 | from .null import replace_null |
| 18 | +from .options import SeamInvalidOptionsError |
17 | 19 | from .strict_url_search_params_serializer import serialize_url_search_params |
18 | 20 |
|
19 | 21 | SDK_HEADERS = { |
@@ -68,7 +70,13 @@ def _handle_response(self, response: Response): |
68 | 70 | self._handle_error_response(response) |
69 | 71 |
|
70 | 72 | if "application/json" in response.headers.get("content-type", ""): |
71 | | - return response.json() |
| 73 | + try: |
| 74 | + return response.json() |
| 75 | + except JSONDecodeError: |
| 76 | + # A body that lies about its content type is handed on as |
| 77 | + # text, so readers report an invalid response instead of |
| 78 | + # leaking a decode error. |
| 79 | + return response.text |
72 | 80 |
|
73 | 81 | return response.text |
74 | 82 |
|
@@ -105,14 +113,24 @@ def __init__( |
105 | 113 | self, |
106 | 114 | base_url: str, |
107 | 115 | auth_headers: Dict[str, str], |
108 | | - retries: Optional[Retry] = DEFAULT_RETRIES, |
| 116 | + retries: Optional[Retry] = None, |
109 | 117 | timeout: Optional[float] = DEFAULT_TIMEOUT, |
110 | 118 | httpx_options: Optional[Dict[str, Any]] = None, |
111 | 119 | **kwargs, |
112 | 120 | ): |
113 | 121 | options = _build_client_options(base_url, timeout, httpx_options, kwargs) |
114 | 122 |
|
115 | 123 | custom_headers = options.pop("headers", {}) |
| 124 | + |
| 125 | + if retries is not None and ( |
| 126 | + options.get("transport") is not None or options.get("mounts") is not None |
| 127 | + ): |
| 128 | + raise SeamInvalidOptionsError( |
| 129 | + "The retries option cannot be combined with a custom transport " |
| 130 | + "or mounts, which bypass the retry transport; wrap your " |
| 131 | + "transport with httpx_retries.RetryTransport instead" |
| 132 | + ) |
| 133 | + |
116 | 134 | self._retry_policy = DEFAULT_RETRIES if retries is None else retries |
117 | 135 |
|
118 | 136 | super().__init__(**options) |
@@ -173,14 +191,24 @@ def __init__( |
173 | 191 | self, |
174 | 192 | base_url: str, |
175 | 193 | auth_headers: Dict[str, str], |
176 | | - retries: Optional[Retry] = DEFAULT_RETRIES, |
| 194 | + retries: Optional[Retry] = None, |
177 | 195 | timeout: Optional[float] = DEFAULT_TIMEOUT, |
178 | 196 | httpx_options: Optional[Dict[str, Any]] = None, |
179 | 197 | **kwargs, |
180 | 198 | ): |
181 | 199 | options = _build_client_options(base_url, timeout, httpx_options, kwargs) |
182 | 200 |
|
183 | 201 | custom_headers = options.pop("headers", {}) |
| 202 | + |
| 203 | + if retries is not None and ( |
| 204 | + options.get("transport") is not None or options.get("mounts") is not None |
| 205 | + ): |
| 206 | + raise SeamInvalidOptionsError( |
| 207 | + "The retries option cannot be combined with a custom transport " |
| 208 | + "or mounts, which bypass the retry transport; wrap your " |
| 209 | + "transport with httpx_retries.RetryTransport instead" |
| 210 | + ) |
| 211 | + |
184 | 212 | self._retry_policy = DEFAULT_RETRIES if retries is None else retries |
185 | 213 |
|
186 | 214 | super().__init__(**options) |
|
0 commit comments