|
| 1 | +"""Read success response payloads defensively. |
| 2 | +
|
| 3 | +A 2xx response with an unexpected shape, e.g., a proxy rewrote the body or |
| 4 | +the response key was renamed, raises the SDK's own error instead of leaking |
| 5 | +a bare KeyError or TypeError from inside a generated route method. |
| 6 | +""" |
| 7 | + |
| 8 | +from typing import Any, Dict, List |
| 9 | + |
| 10 | +from .exceptions import SeamHttpInvalidResponseError |
| 11 | + |
| 12 | + |
| 13 | +def _read_response_key(res: Any, response_key: str, path: str) -> Any: |
| 14 | + if not isinstance(res, dict): |
| 15 | + raise SeamHttpInvalidResponseError( |
| 16 | + path, |
| 17 | + response_key, |
| 18 | + f"got {type(res).__name__} instead of a response object", |
| 19 | + ) |
| 20 | + |
| 21 | + if response_key not in res: |
| 22 | + raise SeamHttpInvalidResponseError( |
| 23 | + path, response_key, "which the response does not contain" |
| 24 | + ) |
| 25 | + |
| 26 | + return res[response_key] |
| 27 | + |
| 28 | + |
| 29 | +def unwrap(res: Any, response_key: str, path: str) -> Dict[str, Any]: |
| 30 | + """Read an object under the response key, or raise for a malformed response.""" |
| 31 | + |
| 32 | + value = _read_response_key(res, response_key, path) |
| 33 | + |
| 34 | + if not isinstance(value, dict): |
| 35 | + raise SeamHttpInvalidResponseError( |
| 36 | + path, response_key, f"got {type(value).__name__} instead of an object" |
| 37 | + ) |
| 38 | + |
| 39 | + return value |
| 40 | + |
| 41 | + |
| 42 | +def unwrap_list(res: Any, response_key: str, path: str) -> List[Any]: |
| 43 | + """Read a list under the response key, or raise for a malformed response.""" |
| 44 | + |
| 45 | + value = _read_response_key(res, response_key, path) |
| 46 | + |
| 47 | + if not isinstance(value, list): |
| 48 | + raise SeamHttpInvalidResponseError( |
| 49 | + path, response_key, f"got {type(value).__name__} instead of a list" |
| 50 | + ) |
| 51 | + |
| 52 | + return value |
0 commit comments