File: _response.py

package info (click to toggle)
python-elastic-transport 8.17.1-2
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 624 kB
  • sloc: python: 6,549; makefile: 31
file content (217 lines) | stat: -rw-r--r-- 6,405 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
#  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]