Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/azure/core/rest/_rest_py3.py: 65%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

115 statements  

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) 

40 

41from ..utils._utils import case_insensitive_dict 

42 

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) 

53 

54ContentType = Union[str, bytes, Iterable[bytes], AsyncIterable[bytes]] 

55 

56################################## CLASSES ###################################### 

57 

58 

59class HttpRequest(HttpRequestBackcompatMixin): 

60 """An HTTP request. 

61 

62 It should be passed to your client's `send_request` method. 

63 

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> 

69 

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 """ 

92 

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 

108 

109 if params: 

110 _format_parameters_helper(self, params) 

111 self._files = None 

112 self._data: Any = None 

113 

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 {}) 

122 

123 if kwargs: 

124 raise TypeError( 

125 "You have passed in kwargs '{}' that are not valid kwargs.".format("', '".join(list(kwargs.keys()))) 

126 ) 

127 

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. 

136 

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 

160 

161 @property 

162 def content(self) -> Any: 

163 """Get's the request's content 

164 

165 :return: The request's content 

166 :rtype: any 

167 """ 

168 return self._data or self._files 

169 

170 def __repr__(self) -> str: 

171 return "<HttpRequest [{}], url: '{}'>".format(self.method, self.url) 

172 

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) 

186 

187 

188class _HttpResponseBase(abc.ABC): 

189 """Base abstract base class for HttpResponses.""" 

190 

191 @property 

192 @abc.abstractmethod 

193 def request(self) -> HttpRequest: 

194 """The request that resulted in this response. 

195 

196 :rtype: ~azure.core.rest.HttpRequest 

197 :return: The request that resulted in this response. 

198 """ 

199 

200 @property 

201 @abc.abstractmethod 

202 def status_code(self) -> int: 

203 """The status code of this response. 

204 

205 :rtype: int 

206 :return: The status code of this response. 

207 """ 

208 

209 @property 

210 @abc.abstractmethod 

211 def headers(self) -> MutableMapping[str, str]: 

212 """The response headers. Must be case-insensitive. 

213 

214 :rtype: MutableMapping[str, str] 

215 :return: The response headers. Must be case-insensitive. 

216 """ 

217 

218 @property 

219 @abc.abstractmethod 

220 def reason(self) -> str: 

221 """The reason phrase for this response. 

222 

223 :rtype: str 

224 :return: The reason phrase for this response. 

225 """ 

226 

227 @property 

228 @abc.abstractmethod 

229 def content_type(self) -> Optional[str]: 

230 """The content type of the response. 

231 

232 :rtype: str 

233 :return: The content type of the response. 

234 """ 

235 

236 @property 

237 @abc.abstractmethod 

238 def is_closed(self) -> bool: 

239 """Whether the network connection has been closed yet. 

240 

241 :rtype: bool 

242 :return: Whether the network connection has been closed yet. 

243 """ 

244 

245 @property 

246 @abc.abstractmethod 

247 def is_stream_consumed(self) -> bool: 

248 """Whether the stream has been consumed. 

249 

250 :rtype: bool 

251 :return: Whether the stream has been consumed. 

252 """ 

253 

254 @property 

255 @abc.abstractmethod 

256 def encoding(self) -> Optional[str]: 

257 """Returns the response encoding. 

258 

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 """ 

264 

265 @encoding.setter 

266 def encoding(self, value: Optional[str]) -> None: 

267 """Sets the response encoding. 

268 

269 :param optional[str] value: The encoding to set 

270 """ 

271 

272 @property 

273 @abc.abstractmethod 

274 def url(self) -> str: 

275 """The URL that resulted in this response. 

276 

277 :rtype: str 

278 :return: The URL that resulted in this response. 

279 """ 

280 

281 @property 

282 @abc.abstractmethod 

283 def content(self) -> bytes: 

284 """Return the response's content in bytes. 

285 

286 :rtype: bytes 

287 :return: The response's content in bytes. 

288 """ 

289 

290 @abc.abstractmethod 

291 def text(self, encoding: Optional[str] = None) -> str: 

292 """Returns the response body as a string. 

293 

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 """ 

299 

300 @abc.abstractmethod 

301 def json(self) -> Any: 

302 """Returns the whole body as a json object. 

303 

304 :return: The JSON deserialized response body 

305 :rtype: any 

306 :raises json.decoder.JSONDecodeError: if the body is not valid JSON. 

307 """ 

308 

309 @abc.abstractmethod 

310 def raise_for_status(self) -> None: 

311 """Raises an HttpResponseError if the response has an error status code. 

312 

313 If response is good, does nothing. 

314 

315 :raises ~azure.core.HttpResponseError: if the object has an error status code. 

316 """ 

317 

318 

319class HttpResponse(_HttpResponseBase): 

320 """Abstract base class for HTTP responses. 

321 

322 Use this abstract base class to create your own transport responses. 

323 

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` 

326 

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 """ 

333 

334 @abc.abstractmethod 

335 def __enter__(self) -> "HttpResponse": ... 

336 

337 @abc.abstractmethod 

338 def __exit__(self, *args: Any) -> None: ... 

339 

340 @abc.abstractmethod 

341 def close(self) -> None: ... 

342 

343 @abc.abstractmethod 

344 def read(self) -> bytes: 

345 """Read the response's bytes. 

346 

347 :return: The read in bytes 

348 :rtype: bytes 

349 """ 

350 

351 @abc.abstractmethod 

352 def iter_raw(self, **kwargs: Any) -> Iterator[bytes]: 

353 """Iterates over the response's bytes. Will not decompress in the process. 

354 

355 :return: An iterator of bytes from the response 

356 :rtype: Iterator[str] 

357 """ 

358 

359 @abc.abstractmethod 

360 def iter_bytes(self, **kwargs: Any) -> Iterator[bytes]: 

361 """Iterates over the response's bytes. Will decompress in the process. 

362 

363 :return: An iterator of bytes from the response 

364 :rtype: Iterator[str] 

365 """ 

366 

367 def __repr__(self) -> str: 

368 content_type_str = ", Content-Type: {}".format(self.content_type) if self.content_type else "" 

369 return "<HttpResponse: {} {}{}>".format(self.status_code, self.reason, content_type_str) 

370 

371 

372class AsyncHttpResponse(_HttpResponseBase, AsyncContextManager["AsyncHttpResponse"]): 

373 """Abstract base class for Async HTTP responses. 

374 

375 Use this abstract base class to create your own transport responses. 

376 

377 Responses implementing this ABC are returned from your async client's `send_request` 

378 method if you pass in an :class:`~azure.core.rest.HttpRequest` 

379 

380 >>> from azure.core.rest import HttpRequest 

381 >>> request = HttpRequest('GET', 'http://www.example.com') 

382 <HttpRequest [GET], url: 'http://www.example.com'> 

383 >>> response = await client.send_request(request) 

384 <AsyncHttpResponse: 200 OK> 

385 """ 

386 

387 @abc.abstractmethod 

388 async def read(self) -> bytes: 

389 """Read the response's bytes into memory. 

390 

391 :return: The response's bytes 

392 :rtype: bytes 

393 """ 

394 

395 @abc.abstractmethod 

396 async def iter_raw(self, **kwargs: Any) -> AsyncIterator[bytes]: 

397 """Asynchronously iterates over the response's bytes. Will not decompress in the process. 

398 

399 :return: An async iterator of bytes from the response 

400 :rtype: AsyncIterator[bytes] 

401 """ 

402 raise NotImplementedError() 

403 # getting around mypy behavior, see https://github.com/python/mypy/issues/10732 

404 yield # pylint: disable=unreachable 

405 

406 @abc.abstractmethod 

407 async def iter_bytes(self, **kwargs: Any) -> AsyncIterator[bytes]: 

408 """Asynchronously iterates over the response's bytes. Will decompress in the process. 

409 

410 :return: An async iterator of bytes from the response 

411 :rtype: AsyncIterator[bytes] 

412 """ 

413 raise NotImplementedError() 

414 # getting around mypy behavior, see https://github.com/python/mypy/issues/10732 

415 yield # pylint: disable=unreachable 

416 

417 @abc.abstractmethod 

418 async def close(self) -> None: ...