Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/azure/core/rest/_rest_py3.py: 62%
120 statements
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-07 06:33 +0000
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-07 06:33 +0000
1# --------------------------------------------------------------------------
2#
3# Copyright (c) Microsoft Corporation. All rights reserved.
4#
5# The MIT License (MIT)
6#
7# Permission is hereby granted, free of charge, to any person obtaining a copy
8# of this software and associated documentation files (the ""Software""), to
9# deal in the Software without restriction, including without limitation the
10# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11# sell copies of the Software, and to permit persons to whom the Software is
12# furnished to do so, subject to the following conditions:
13#
14# The above copyright notice and this permission notice shall be included in
15# all copies or substantial portions of the Software.
16#
17# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23# IN THE SOFTWARE.
24#
25# --------------------------------------------------------------------------
26import abc
27import copy
28from typing import (
29 Any,
30 AsyncIterable,
31 AsyncIterator,
32 Iterable,
33 Iterator,
34 Optional,
35 Union,
36 MutableMapping,
37 Dict,
38 AsyncContextManager,
39)
41from ..utils._utils import case_insensitive_dict
43from ._helpers import (
44 ParamsType,
45 FilesType,
46 set_json_body,
47 set_multipart_body,
48 set_urlencoded_body,
49 _format_parameters_helper,
50 HttpRequestBackcompatMixin,
51 set_content_body,
52)
54ContentType = Union[str, bytes, Iterable[bytes], AsyncIterable[bytes]]
56################################## CLASSES ######################################
59class HttpRequest(HttpRequestBackcompatMixin):
60 """An HTTP request.
62 It should be passed to your client's `send_request` method.
64 >>> from azure.core.rest import HttpRequest
65 >>> request = HttpRequest('GET', 'http://www.example.com')
66 <HttpRequest [GET], url: 'http://www.example.com'>
67 >>> response = client.send_request(request)
68 <HttpResponse: 200 OK>
70 :param str method: HTTP method (GET, HEAD, etc.)
71 :param str url: The url for your request
72 :keyword mapping params: Query parameters to be mapped into your URL. Your input
73 should be a mapping of query name to query value(s).
74 :keyword mapping headers: HTTP headers you want in your request. Your input should
75 be a mapping of header name to header value.
76 :keyword any json: A JSON serializable object. We handle JSON-serialization for your
77 object, so use this for more complicated data structures than `data`.
78 :keyword content: Content you want in your request body. Think of it as the kwarg you should input
79 if your data doesn't fit into `json`, `data`, or `files`. Accepts a bytes type, or a generator
80 that yields bytes.
81 :paramtype content: str or bytes or iterable[bytes] or asynciterable[bytes]
82 :keyword dict data: Form data you want in your request body. Use for form-encoded data, i.e.
83 HTML forms.
84 :keyword mapping files: Files you want to in your request body. Use for uploading files with
85 multipart encoding. Your input should be a mapping of file name to file content.
86 Use the `data` kwarg in addition if you want to include non-file data files as part of your request.
87 :ivar str url: The URL this request is against.
88 :ivar str method: The method type of this request.
89 :ivar mapping headers: The HTTP headers you passed in to your request
90 :ivar any content: The content passed in for the request
91 """
93 def __init__(
94 self,
95 method: str,
96 url: str,
97 *,
98 params: Optional[ParamsType] = None,
99 headers: Optional[MutableMapping[str, str]] = None,
100 json: Any = None,
101 content: Optional[ContentType] = None,
102 data: Optional[Dict[str, Any]] = None,
103 files: Optional[FilesType] = None,
104 **kwargs: Any
105 ):
106 self.url = url
107 self.method = method
109 if params:
110 _format_parameters_helper(self, params)
111 self._files = None
112 self._data: Any = None
114 default_headers = self._set_body(
115 content=content,
116 data=data,
117 files=files,
118 json=json,
119 )
120 self.headers: MutableMapping[str, str] = case_insensitive_dict(default_headers)
121 self.headers.update(headers or {})
123 if kwargs:
124 raise TypeError(
125 "You have passed in kwargs '{}' that are not valid kwargs.".format("', '".join(list(kwargs.keys())))
126 )
128 def _set_body(
129 self,
130 content: Optional[ContentType] = None,
131 data: Optional[Dict[str, Any]] = None,
132 files: Optional[FilesType] = None,
133 json: Any = None,
134 ) -> MutableMapping[str, str]:
135 """Sets the body of the request, and returns the default headers.
137 :param content: Content you want in your request body.
138 :type content: str or bytes or iterable[bytes] or asynciterable[bytes]
139 :param dict data: Form data you want in your request body.
140 :param dict files: Files you want to in your request body.
141 :param any json: A JSON serializable object.
142 :return: The default headers for the request
143 :rtype: MutableMapping[str, str]
144 """
145 default_headers: MutableMapping[str, str] = {}
146 if data is not None and not isinstance(data, dict):
147 # should we warn?
148 content = data
149 if content is not None:
150 default_headers, self._data = set_content_body(content)
151 return default_headers
152 if json is not None:
153 default_headers, self._data = set_json_body(json)
154 return default_headers
155 if files:
156 default_headers, self._files = set_multipart_body(files)
157 if data:
158 default_headers, self._data = set_urlencoded_body(data, has_files=bool(files))
159 return default_headers
161 @property
162 def content(self) -> Any:
163 """Get's the request's content
165 :return: The request's content
166 :rtype: any
167 """
168 return self._data or self._files
170 def __repr__(self) -> str:
171 return "<HttpRequest [{}], url: '{}'>".format(self.method, self.url)
173 def __deepcopy__(self, memo: Optional[Dict[int, Any]] = None) -> "HttpRequest":
174 try:
175 request = HttpRequest(
176 method=self.method,
177 url=self.url,
178 headers=self.headers,
179 )
180 request._data = copy.deepcopy(self._data, memo)
181 request._files = copy.deepcopy(self._files, memo)
182 self._add_backcompat_properties(request, memo)
183 return request
184 except (ValueError, TypeError):
185 return copy.copy(self)
188class _HttpResponseBase(abc.ABC):
189 """Base abstract base class for HttpResponses."""
191 @property
192 @abc.abstractmethod
193 def request(self) -> HttpRequest:
194 """The request that resulted in this response.
196 :rtype: ~azure.core.rest.HttpRequest
197 :return: The request that resulted in this response.
198 """
200 @property
201 @abc.abstractmethod
202 def status_code(self) -> int:
203 """The status code of this response.
205 :rtype: int
206 :return: The status code of this response.
207 """
209 @property
210 @abc.abstractmethod
211 def headers(self) -> MutableMapping[str, str]:
212 """The response headers. Must be case-insensitive.
214 :rtype: MutableMapping[str, str]
215 :return: The response headers. Must be case-insensitive.
216 """
218 @property
219 @abc.abstractmethod
220 def reason(self) -> str:
221 """The reason phrase for this response.
223 :rtype: str
224 :return: The reason phrase for this response.
225 """
227 @property
228 @abc.abstractmethod
229 def content_type(self) -> Optional[str]:
230 """The content type of the response.
232 :rtype: str
233 :return: The content type of the response.
234 """
236 @property
237 @abc.abstractmethod
238 def is_closed(self) -> bool:
239 """Whether the network connection has been closed yet.
241 :rtype: bool
242 :return: Whether the network connection has been closed yet.
243 """
245 @property
246 @abc.abstractmethod
247 def is_stream_consumed(self) -> bool:
248 """Whether the stream has been consumed.
250 :rtype: bool
251 :return: Whether the stream has been consumed.
252 """
254 @property
255 @abc.abstractmethod
256 def encoding(self) -> Optional[str]:
257 """Returns the response encoding.
259 :return: The response encoding. We either return the encoding set by the user,
260 or try extracting the encoding from the response's content type. If all fails,
261 we return `None`.
262 :rtype: optional[str]
263 """
265 @encoding.setter
266 def encoding(self, value: Optional[str]) -> None:
267 """Sets the response encoding.
269 :param optional[str] value: The encoding to set
270 """
272 @property
273 @abc.abstractmethod
274 def url(self) -> str:
275 """The URL that resulted in this response.
277 :rtype: str
278 :return: The URL that resulted in this response.
279 """
281 @property
282 @abc.abstractmethod
283 def content(self) -> bytes:
284 """Return the response's content in bytes.
286 :rtype: bytes
287 :return: The response's content in bytes.
288 """
290 @abc.abstractmethod
291 def text(self, encoding: Optional[str] = None) -> str:
292 """Returns the response body as a string.
294 :param optional[str] encoding: The encoding you want to decode the text with. Can
295 also be set independently through our encoding property
296 :return: The response's content decoded as a string.
297 :rtype: str
298 """
300 @abc.abstractmethod
301 def json(self) -> Any:
302 """Returns the whole body as a json object.
304 :return: The JSON deserialized response body
305 :rtype: any
306 :raises json.decoder.JSONDecodeError or ValueError (in python 2.7) if object is not JSON decodable:
307 """
309 @abc.abstractmethod
310 def raise_for_status(self) -> None:
311 """Raises an HttpResponseError if the response has an error status code.
313 If response is good, does nothing.
315 :raises ~azure.core.HttpResponseError if the object has an error status code.:
316 """
319class HttpResponse(_HttpResponseBase):
320 """Abstract base class for HTTP responses.
322 Use this abstract base class to create your own transport responses.
324 Responses implementing this ABC are returned from your client's `send_request` method
325 if you pass in an :class:`~azure.core.rest.HttpRequest`
327 >>> from azure.core.rest import HttpRequest
328 >>> request = HttpRequest('GET', 'http://www.example.com')
329 <HttpRequest [GET], url: 'http://www.example.com'>
330 >>> response = client.send_request(request)
331 <HttpResponse: 200 OK>
332 """
334 @abc.abstractmethod
335 def __enter__(self) -> "HttpResponse":
336 ...
338 @abc.abstractmethod
339 def __exit__(self, *args: Any) -> None:
340 ...
342 @abc.abstractmethod
343 def close(self) -> None:
344 ...
346 @abc.abstractmethod
347 def read(self) -> bytes:
348 """Read the response's bytes.
350 :return: The read in bytes
351 :rtype: bytes
352 """
354 @abc.abstractmethod
355 def iter_raw(self, **kwargs: Any) -> Iterator[bytes]:
356 """Iterates over the response's bytes. Will not decompress in the process.
358 :return: An iterator of bytes from the response
359 :rtype: Iterator[str]
360 """
362 @abc.abstractmethod
363 def iter_bytes(self, **kwargs: Any) -> Iterator[bytes]:
364 """Iterates over the response's bytes. Will decompress in the process.
366 :return: An iterator of bytes from the response
367 :rtype: Iterator[str]
368 """
370 def __repr__(self) -> str:
371 content_type_str = ", Content-Type: {}".format(self.content_type) if self.content_type else ""
372 return "<HttpResponse: {} {}{}>".format(self.status_code, self.reason, content_type_str)
375class AsyncHttpResponse(_HttpResponseBase, AsyncContextManager["AsyncHttpResponse"]):
376 """Abstract base class for Async HTTP responses.
378 Use this abstract base class to create your own transport responses.
380 Responses implementing this ABC are returned from your async client's `send_request`
381 method if you pass in an :class:`~azure.core.rest.HttpRequest`
383 >>> from azure.core.rest import HttpRequest
384 >>> request = HttpRequest('GET', 'http://www.example.com')
385 <HttpRequest [GET], url: 'http://www.example.com'>
386 >>> response = await client.send_request(request)
387 <AsyncHttpResponse: 200 OK>
388 """
390 @abc.abstractmethod
391 async def read(self) -> bytes:
392 """Read the response's bytes into memory.
394 :return: The response's bytes
395 :rtype: bytes
396 """
398 @abc.abstractmethod
399 async def iter_raw(self, **kwargs: Any) -> AsyncIterator[bytes]:
400 """Asynchronously iterates over the response's bytes. Will not decompress in the process.
402 :return: An async iterator of bytes from the response
403 :rtype: AsyncIterator[bytes]
404 """
405 raise NotImplementedError()
406 # getting around mypy behavior, see https://github.com/python/mypy/issues/10732
407 yield # pylint: disable=unreachable
409 @abc.abstractmethod
410 async def iter_bytes(self, **kwargs: Any) -> AsyncIterator[bytes]:
411 """Asynchronously iterates over the response's bytes. Will decompress in the process.
413 :return: An async iterator of bytes from the response
414 :rtype: AsyncIterator[bytes]
415 """
416 raise NotImplementedError()
417 # getting around mypy behavior, see https://github.com/python/mypy/issues/10732
418 yield # pylint: disable=unreachable
420 @abc.abstractmethod
421 async def close(self) -> None:
422 ...