elastic_transport/_response.py (136 lines of code) (raw):

# Licensed to Elasticsearch B.V. under one or more contributor # license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright # ownership. Elasticsearch B.V. licenses this file to you under # the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. from typing import ( Any, Dict, Generic, Iterator, List, NoReturn, Tuple, TypeVar, Union, overload, ) from ._models import ApiResponseMeta _BodyType = TypeVar("_BodyType") _ObjectBodyType = TypeVar("_ObjectBodyType") _ListItemBodyType = TypeVar("_ListItemBodyType") class ApiResponse(Generic[_BodyType]): """Base class for all API response classes""" __slots__ = ("_body", "_meta") def __init__( self, *args: Any, **kwargs: Any, ): def _raise_typeerror() -> NoReturn: raise TypeError("Must pass 'meta' and 'body' to ApiResponse") from None # Working around pre-releases of elasticsearch-python # that would use raw=... instead of body=... try: if bool(args) == bool(kwargs): _raise_typeerror() elif args and len(args) == 2: body, meta = args elif kwargs and "raw" in kwargs: body = kwargs.pop("raw") meta = kwargs.pop("meta") kwargs.pop("body_cls", None) elif kwargs and "body" in kwargs: body = kwargs.pop("body") meta = kwargs.pop("meta") kwargs.pop("body_cls", None) else: _raise_typeerror() except KeyError: _raise_typeerror() # If there are still kwargs left over # and we're not in positional mode... if not args and kwargs: _raise_typeerror() self._body = body self._meta = meta def __repr__(self) -> str: return f"{type(self).__name__}({self.body!r})" def __contains__(self, item: Any) -> bool: return item in self._body def __eq__(self, other: object) -> bool: if isinstance(other, ApiResponse): other = other.body return self._body == other # type: ignore[no-any-return] def __ne__(self, other: object) -> bool: if isinstance(other, ApiResponse): other = other.body return self._body != other # type: ignore[no-any-return] def __getitem__(self, item: Any) -> Any: return self._body[item] def __getattr__(self, attr: str) -> Any: return getattr(self._body, attr) def __getstate__(self) -> Tuple[_BodyType, ApiResponseMeta]: return self._body, self._meta def __setstate__(self, state: Tuple[_BodyType, ApiResponseMeta]) -> None: self._body, self._meta = state def __len__(self) -> int: return len(self._body) def __iter__(self) -> Iterator[Any]: return iter(self._body) def __str__(self) -> str: return str(self._body) def __bool__(self) -> bool: return bool(self._body) @property def meta(self) -> ApiResponseMeta: """Response metadata""" return self._meta # type: ignore[no-any-return] @property def body(self) -> _BodyType: """User-friendly view into the raw response with type hints if applicable""" return self._body # type: ignore[no-any-return] @property def raw(self) -> _BodyType: return self.body class TextApiResponse(ApiResponse[str]): """API responses which are text such as 'text/plain' or 'text/csv'""" def __iter__(self) -> Iterator[str]: return iter(self.body) def __getitem__(self, item: Union[int, slice]) -> str: return self.body[item] @property def body(self) -> str: return self._body # type: ignore[no-any-return] class BinaryApiResponse(ApiResponse[bytes]): """API responses which are a binary response such as Mapbox vector tiles""" def __iter__(self) -> Iterator[int]: return iter(self.body) @overload def __getitem__(self, item: slice) -> bytes: ... @overload def __getitem__(self, item: int) -> int: ... def __getitem__(self, item: Union[int, slice]) -> Union[int, bytes]: return self.body[item] @property def body(self) -> bytes: return self._body # type: ignore[no-any-return] class HeadApiResponse(ApiResponse[bool]): """API responses which are for an 'exists' / HEAD API request""" def __init__(self, meta: ApiResponseMeta): super().__init__(body=200 <= meta.status < 300, meta=meta) def __bool__(self) -> bool: return 200 <= self.meta.status < 300 @property def body(self) -> bool: return bool(self) class ObjectApiResponse(Generic[_ObjectBodyType], ApiResponse[Dict[str, Any]]): """API responses which are for a JSON object""" def __getitem__(self, item: str) -> Any: return self.body[item] # type: ignore[index] def __iter__(self) -> Iterator[str]: return iter(self._body) @property def body(self) -> _ObjectBodyType: # type: ignore[override] return self._body # type: ignore[no-any-return] class ListApiResponse( Generic[_ListItemBodyType], ApiResponse[List[Any]], ): """API responses which are a list of items. Can be NDJSON or a JSON list""" @overload def __getitem__(self, item: slice) -> List[_ListItemBodyType]: ... @overload def __getitem__(self, item: int) -> _ListItemBodyType: ... def __getitem__( self, item: Union[int, slice] ) -> Union[_ListItemBodyType, List[_ListItemBodyType]]: return self.body[item] def __iter__(self) -> Iterator[_ListItemBodyType]: return iter(self.body) @property def body(self) -> List[_ListItemBodyType]: return self._body # type: ignore[no-any-return]