Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/azure/core/pipeline/transport/_base.py: 33%
249 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# --------------------------------------------------------------------------
26from __future__ import annotations
27import abc
28from email.message import Message
29import json
30import logging
31import time
32import copy
33from urllib.parse import urlparse
34import xml.etree.ElementTree as ET
36from typing import (
37 Generic,
38 TypeVar,
39 IO,
40 Union,
41 Any,
42 Mapping,
43 Optional,
44 Tuple,
45 Iterator,
46 Type,
47 Dict,
48 List,
49 Sequence,
50 MutableMapping,
51 ContextManager,
52 TYPE_CHECKING,
53)
55from http.client import HTTPResponse as _HTTPResponse
57from azure.core.exceptions import HttpResponseError
58from azure.core.pipeline.policies import SansIOHTTPPolicy
59from ...utils._utils import case_insensitive_dict
60from ...utils._pipeline_transport_rest_shared import (
61 _format_parameters_helper,
62 _prepare_multipart_body_helper,
63 _serialize_request,
64 _format_data_helper,
65 BytesIOSocket,
66 _decode_parts_helper,
67 _get_raw_parts_helper,
68 _parts_helper,
69)
72HTTPResponseType = TypeVar("HTTPResponseType")
73HTTPRequestType = TypeVar("HTTPRequestType")
74DataType = Union[bytes, str, Dict[str, Union[str, int]]]
76if TYPE_CHECKING:
77 # We need a transport to define a pipeline, this "if" avoid a circular import
78 from azure.core.pipeline import Pipeline
80_LOGGER = logging.getLogger(__name__)
82binary_type = str
85def _format_url_section(template, **kwargs: Dict[str, str]) -> str:
86 """String format the template with the kwargs, auto-skip sections of the template that are NOT in the kwargs.
88 By default in Python, "format" will raise a KeyError if a template element is not found. Here the section between
89 the slashes will be removed from the template instead.
91 This is used for API like Storage, where when Swagger has template section not defined as parameter.
93 :param str template: a string template to fill
94 :rtype: str
95 :returns: Template completed
96 """
97 last_template = template
98 components = template.split("/")
99 while components:
100 try:
101 return template.format(**kwargs)
102 except KeyError as key:
103 formatted_components = template.split("/")
104 components = [c for c in formatted_components if "{{{}}}".format(key.args[0]) not in c]
105 template = "/".join(components)
106 if last_template == template:
107 raise ValueError(
108 f"The value provided for the url part '{template}' was incorrect, and resulted in an invalid url"
109 ) from key
110 last_template = template
111 return last_template
114def _urljoin(base_url: str, stub_url: str) -> str:
115 """Append to end of base URL without losing query parameters.
117 :param str base_url: The base URL.
118 :param str stub_url: Section to append to the end of the URL path.
119 :returns: The updated URL.
120 :rtype: str
121 """
122 parsed_base_url = urlparse(base_url)
124 # Can't use "urlparse" on a partial url, we get incorrect parsing for things like
125 # document:build?format=html&api-version=2019-05-01
126 split_url = stub_url.split("?", 1)
127 stub_url_path = split_url.pop(0)
128 stub_url_query = split_url.pop() if split_url else None
130 # Note that _replace is a public API named that way to avoid conflicts in namedtuple
131 # https://docs.python.org/3/library/collections.html?highlight=namedtuple#collections.namedtuple
132 parsed_base_url = parsed_base_url._replace(
133 path=parsed_base_url.path.rstrip("/") + "/" + stub_url_path,
134 )
135 if stub_url_query:
136 query_params = [stub_url_query]
137 if parsed_base_url.query:
138 query_params.insert(0, parsed_base_url.query)
139 parsed_base_url = parsed_base_url._replace(query="&".join(query_params))
140 return parsed_base_url.geturl()
143class HttpTransport(ContextManager["HttpTransport"], abc.ABC, Generic[HTTPRequestType, HTTPResponseType]):
144 """An http sender ABC."""
146 @abc.abstractmethod
147 def send(self, request: HTTPRequestType, **kwargs: Any) -> HTTPResponseType:
148 """Send the request using this HTTP sender.
150 :param request: The pipeline request object
151 :type request: ~azure.core.transport.HTTPRequest
152 :return: The pipeline response object.
153 :rtype: ~azure.core.pipeline.transport.HttpResponse
154 """
156 @abc.abstractmethod
157 def open(self) -> None:
158 """Assign new session if one does not already exist."""
160 @abc.abstractmethod
161 def close(self) -> None:
162 """Close the session if it is not externally owned."""
164 def sleep(self, duration: float) -> None:
165 """Sleep for the specified duration.
167 You should always ask the transport to sleep, and not call directly
168 the stdlib. This is mostly important in async, as the transport
169 may not use asyncio but other implementations like trio and they have their own
170 way to sleep, but to keep design
171 consistent, it's cleaner to always ask the transport to sleep and let the transport
172 implementor decide how to do it.
174 :param float duration: The number of seconds to sleep.
175 """
176 time.sleep(duration)
179class HttpRequest:
180 """Represents an HTTP request.
182 URL can be given without query parameters, to be added later using "format_parameters".
184 :param str method: HTTP method (GET, HEAD, etc.)
185 :param str url: At least complete scheme/host/path
186 :param dict[str,str] headers: HTTP headers
187 :param files: Dictionary of ``'name': file-like-objects`` (or ``{'name': file-tuple}``) for multipart
188 encoding upload. ``file-tuple`` can be a 2-tuple ``('filename', fileobj)``, 3-tuple
189 ``('filename', fileobj, 'content_type')`` or a 4-tuple
190 ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content_type'`` is a string
191 defining the content type of the given file and ``custom_headers``
192 a dict-like object containing additional headers to add for the file.
193 :type files: dict[str, tuple[str, IO, str, dict]] or dict[str, IO]
194 :param data: Body to be sent.
195 :type data: bytes or dict (for form)
196 """
198 def __init__(
199 self,
200 method: str,
201 url: str,
202 headers: Optional[Mapping[str, str]] = None,
203 files: Optional[Any] = None,
204 data: Optional[DataType] = None,
205 ) -> None:
206 self.method = method
207 self.url = url
208 self.headers: MutableMapping[str, str] = case_insensitive_dict(headers)
209 self.files: Optional[Any] = files
210 self.data: Optional[DataType] = data
211 self.multipart_mixed_info: Optional[Tuple[Sequence[Any], Sequence[Any], Optional[str], Dict[str, Any]]] = None
213 def __repr__(self) -> str:
214 return "<HttpRequest [{}], url: '{}'>".format(self.method, self.url)
216 def __deepcopy__(self, memo: Optional[Dict[int, Any]] = None) -> "HttpRequest":
217 try:
218 data = copy.deepcopy(self.body, memo)
219 files = copy.deepcopy(self.files, memo)
220 request = HttpRequest(self.method, self.url, self.headers, files, data)
221 request.multipart_mixed_info = self.multipart_mixed_info
222 return request
223 except (ValueError, TypeError):
224 return copy.copy(self)
226 @property
227 def query(self) -> Dict[str, str]:
228 """The query parameters of the request as a dict.
230 :rtype: dict[str, str]
231 :return: The query parameters of the request as a dict.
232 """
233 query = urlparse(self.url).query
234 if query:
235 return {p[0]: p[-1] for p in [p.partition("=") for p in query.split("&")]}
236 return {}
238 @property
239 def body(self) -> Optional[DataType]:
240 """Alias to data.
242 :rtype: bytes or str or dict or None
243 :return: The body of the request.
244 """
245 return self.data
247 @body.setter
248 def body(self, value: Optional[DataType]) -> None:
249 self.data = value
251 @staticmethod
252 def _format_data(data: Union[str, IO]) -> Union[Tuple[None, str], Tuple[Optional[str], IO, str]]:
253 """Format field data according to whether it is a stream or
254 a string for a form-data request.
256 :param data: The request field data.
257 :type data: str or file-like object.
258 :rtype: tuple[str, IO, str] or tuple[None, str]
259 :return: A tuple of (data name, data IO, "application/octet-stream") or (None, data str)
260 """
261 return _format_data_helper(data)
263 def format_parameters(self, params: Dict[str, str]) -> None:
264 """Format parameters into a valid query string.
265 It's assumed all parameters have already been quoted as
266 valid URL strings.
268 :param dict params: A dictionary of parameters.
269 """
270 return _format_parameters_helper(self, params)
272 def set_streamed_data_body(self, data: Any) -> None:
273 """Set a streamable data body.
275 :param data: The request field data.
276 :type data: stream or generator or asyncgenerator
277 """
278 if not isinstance(data, binary_type) and not any(
279 hasattr(data, attr) for attr in ["read", "__iter__", "__aiter__"]
280 ):
281 raise TypeError("A streamable data source must be an open file-like object or iterable.")
282 self.data = data
283 self.files = None
285 def set_text_body(self, data: str) -> None:
286 """Set a text as body of the request.
288 :param data: A text to send as body.
289 :type data: str
290 """
291 if data is None:
292 self.data = None
293 else:
294 self.data = data
295 self.headers["Content-Length"] = str(len(self.data))
296 self.files = None
298 def set_xml_body(self, data: Any) -> None:
299 """Set an XML element tree as the body of the request.
301 :param data: The request field data.
302 :type data: XML node
303 """
304 if data is None:
305 self.data = None
306 else:
307 bytes_data: bytes = ET.tostring(data, encoding="utf8")
308 self.data = bytes_data.replace(b"encoding='utf8'", b"encoding='utf-8'")
309 self.headers["Content-Length"] = str(len(self.data))
310 self.files = None
312 def set_json_body(self, data: Any) -> None:
313 """Set a JSON-friendly object as the body of the request.
315 :param data: A JSON serializable object
316 :type data: dict
317 """
318 if data is None:
319 self.data = None
320 else:
321 self.data = json.dumps(data)
322 self.headers["Content-Length"] = str(len(self.data))
323 self.files = None
325 def set_formdata_body(self, data: Optional[Dict[str, str]] = None) -> None:
326 """Set form-encoded data as the body of the request.
328 :param data: The request field data.
329 :type data: dict
330 """
331 if data is None:
332 data = {}
333 content_type = self.headers.pop("Content-Type", None) if self.headers else None
335 if content_type and content_type.lower() == "application/x-www-form-urlencoded":
336 self.data = {f: d for f, d in data.items() if d is not None}
337 self.files = None
338 else: # Assume "multipart/form-data"
339 self.files = {f: self._format_data(d) for f, d in data.items() if d is not None}
340 self.data = None
342 def set_bytes_body(self, data: bytes) -> None:
343 """Set generic bytes as the body of the request.
345 Will set content-length.
347 :param data: The request field data.
348 :type data: bytes
349 """
350 if data:
351 self.headers["Content-Length"] = str(len(data))
352 self.data = data
353 self.files = None
355 def set_multipart_mixed(
356 self,
357 *requests: "HttpRequest",
358 policies: Optional[List[SansIOHTTPPolicy[HTTPRequestType, HTTPResponseType]]] = None,
359 boundary: Optional[str] = None,
360 **kwargs: Any,
361 ) -> None:
362 """Set the part of a multipart/mixed.
364 Only supported args for now are HttpRequest objects.
366 boundary is optional, and one will be generated if you don't provide one.
367 Note that no verification are made on the boundary, this is considered advanced
368 enough so you know how to respect RFC1341 7.2.1 and provide a correct boundary.
370 Any additional kwargs will be passed into the pipeline context for per-request policy
371 configuration.
373 :param requests: The requests to add to the multipart/mixed
374 :type requests: ~azure.core.pipeline.transport.HttpRequest
375 :keyword list[SansIOHTTPPolicy] policies: SansIOPolicy to apply at preparation time
376 :keyword str boundary: Optional boundary
377 """
378 policies = policies or []
379 self.multipart_mixed_info = (
380 requests,
381 policies,
382 boundary,
383 kwargs,
384 )
386 def prepare_multipart_body(self, content_index: int = 0) -> int:
387 """Will prepare the body of this request according to the multipart information.
389 This call assumes the on_request policies have been applied already in their
390 correct context (sync/async)
392 Does nothing if "set_multipart_mixed" was never called.
394 :param int content_index: The current index of parts within the batch message.
395 :returns: The updated index after all parts in this request have been added.
396 :rtype: int
397 """
398 return _prepare_multipart_body_helper(self, content_index)
400 def serialize(self) -> bytes:
401 """Serialize this request using application/http spec.
403 :rtype: bytes
404 :return: The requests serialized as HTTP low-level message in bytes.
405 """
406 return _serialize_request(self)
409class _HttpResponseBase:
410 """Represent a HTTP response.
412 No body is defined here on purpose, since async pipeline
413 will provide async ways to access the body
414 Full in-memory using "body" as bytes.
416 :param request: The request.
417 :type request: ~azure.core.pipeline.transport.HttpRequest
418 :param internal_response: The object returned from the HTTP library.
419 :type internal_response: any
420 :param int block_size: Defaults to 4096 bytes.
421 """
423 def __init__(
424 self,
425 request: "HttpRequest",
426 internal_response: Any,
427 block_size: Optional[int] = None,
428 ) -> None:
429 self.request: HttpRequest = request
430 self.internal_response = internal_response
431 # This is actually never None, and set by all implementations after the call to
432 # __init__ of this class. This class is also a legacy impl, so it's risky to change it
433 # for low benefits The new "rest" implementation does define correctly status_code
434 # as non-optional.
435 self.status_code: int = None # type: ignore
436 self.headers: MutableMapping[str, str] = {}
437 self.reason: Optional[str] = None
438 self.content_type: Optional[str] = None
439 self.block_size: int = block_size or 4096 # Default to same as Requests
441 def body(self) -> bytes:
442 """Return the whole body as bytes in memory.
444 Sync implementer should load the body in memory if they can.
445 Async implementer should rely on async load_body to have been called first.
447 :rtype: bytes
448 :return: The whole body as bytes in memory.
449 """
450 raise NotImplementedError()
452 def text(self, encoding: Optional[str] = None) -> str:
453 """Return the whole body as a string.
455 .. seealso:: ~body()
457 :param str encoding: The encoding to apply. If None, use "utf-8" with BOM parsing (utf-8-sig).
458 Implementation can be smarter if they want (using headers or chardet).
459 :rtype: str
460 :return: The whole body as a string.
461 """
462 if encoding == "utf-8" or encoding is None:
463 encoding = "utf-8-sig"
464 return self.body().decode(encoding)
466 def _decode_parts(
467 self,
468 message: Message,
469 http_response_type: Type["_HttpResponseBase"],
470 requests: Sequence[HttpRequest],
471 ) -> List["HttpResponse"]:
472 """Rebuild an HTTP response from pure string.
474 :param ~email.message.Message message: The HTTP message as an email object
475 :param type http_response_type: The type of response to return
476 :param list[HttpRequest] requests: The requests that were batched together
477 :rtype: list[HttpResponse]
478 :return: The list of HttpResponse
479 """
480 return _decode_parts_helper(self, message, http_response_type, requests, _deserialize_response)
482 def _get_raw_parts(
483 self, http_response_type: Optional[Type["_HttpResponseBase"]] = None
484 ) -> Iterator["HttpResponse"]:
485 """Assuming this body is multipart, return the iterator or parts.
487 If parts are application/http use http_response_type or HttpClientTransportResponse
488 as envelope.
490 :param type http_response_type: The type of response to return
491 :rtype: iterator[HttpResponse]
492 :return: The iterator of HttpResponse
493 """
494 return _get_raw_parts_helper(self, http_response_type or HttpClientTransportResponse)
496 def raise_for_status(self) -> None:
497 """Raises an HttpResponseError if the response has an error status code.
498 If response is good, does nothing.
499 """
500 if not self.status_code or self.status_code >= 400:
501 raise HttpResponseError(response=self)
503 def __repr__(self) -> str:
504 content_type_str = ", Content-Type: {}".format(self.content_type) if self.content_type else ""
505 return "<{}: {} {}{}>".format(type(self).__name__, self.status_code, self.reason, content_type_str)
508class HttpResponse(_HttpResponseBase): # pylint: disable=abstract-method
509 def stream_download(self, pipeline: Pipeline[HttpRequest, "HttpResponse"], **kwargs: Any) -> Iterator[bytes]:
510 """Generator for streaming request body data.
512 Should be implemented by sub-classes if streaming download
513 is supported.
515 :param pipeline: The pipeline object
516 :type pipeline: ~azure.core.pipeline.Pipeline
517 :rtype: iterator[bytes]
518 :return: The generator of bytes connected to the socket
519 """
520 raise NotImplementedError("stream_download is not implemented.")
522 def parts(self) -> Iterator["HttpResponse"]:
523 """Assuming the content-type is multipart/mixed, will return the parts as an iterator.
525 :rtype: iterator[HttpResponse]
526 :return: The iterator of HttpResponse if request was multipart/mixed
527 :raises ValueError: If the content is not multipart/mixed
528 """
529 return _parts_helper(self)
532class _HttpClientTransportResponse(_HttpResponseBase):
533 """Create a HTTPResponse from an http.client response.
535 Body will NOT be read by the constructor. Call "body()" to load the body in memory if necessary.
537 :param HttpRequest request: The request.
538 :param httpclient_response: The object returned from an HTTP(S)Connection from http.client
539 :type httpclient_response: http.client.HTTPResponse
540 """
542 def __init__(self, request, httpclient_response):
543 super(_HttpClientTransportResponse, self).__init__(request, httpclient_response)
544 self.status_code = httpclient_response.status
545 self.headers = case_insensitive_dict(httpclient_response.getheaders())
546 self.reason = httpclient_response.reason
547 self.content_type = self.headers.get("Content-Type")
548 self.data = None
550 def body(self):
551 if self.data is None:
552 self.data = self.internal_response.read()
553 return self.data
556class HttpClientTransportResponse(_HttpClientTransportResponse, HttpResponse): # pylint: disable=abstract-method
557 """Create a HTTPResponse from an http.client response.
559 Body will NOT be read by the constructor. Call "body()" to load the body in memory if necessary.
560 """
563def _deserialize_response(http_response_as_bytes, http_request, http_response_type=HttpClientTransportResponse):
564 """Deserialize a HTTPResponse from a string.
566 :param bytes http_response_as_bytes: The HTTP response as bytes.
567 :param HttpRequest http_request: The request to store in the response.
568 :param type http_response_type: The type of response to return
569 :rtype: HttpResponse
570 :return: The HTTP response from those low-level bytes.
571 """
572 local_socket = BytesIOSocket(http_response_as_bytes)
573 response = _HTTPResponse(local_socket, method=http_request.method)
574 response.begin()
575 return http_response_type(http_request, response)
578class PipelineClientBase:
579 """Base class for pipeline clients.
581 :param str base_url: URL for the request.
582 """
584 def __init__(self, base_url: str):
585 self._base_url = base_url
587 def _request(
588 self,
589 method: str,
590 url: str,
591 params: Optional[Dict[str, str]],
592 headers: Optional[Dict[str, str]],
593 content: Any,
594 form_content: Optional[Dict[str, Any]],
595 stream_content: Any,
596 ) -> HttpRequest:
597 """Create HttpRequest object.
599 If content is not None, guesses will be used to set the right body:
600 - If content is an XML tree, will serialize as XML
601 - If content-type starts by "text/", set the content as text
602 - Else, try JSON serialization
604 :param str method: HTTP method (GET, HEAD, etc.)
605 :param str url: URL for the request.
606 :param dict params: URL query parameters.
607 :param dict headers: Headers
608 :param content: The body content
609 :type content: bytes or str or dict
610 :param dict form_content: Form content
611 :param stream_content: The body content as a stream
612 :type stream_content: stream or generator or asyncgenerator
613 :return: An HttpRequest object
614 :rtype: ~azure.core.pipeline.transport.HttpRequest
615 """
616 request = HttpRequest(method, self.format_url(url))
618 if params:
619 request.format_parameters(params)
621 if headers:
622 request.headers.update(headers)
624 if content is not None:
625 content_type = request.headers.get("Content-Type")
626 if isinstance(content, ET.Element):
627 request.set_xml_body(content)
628 # https://github.com/Azure/azure-sdk-for-python/issues/12137
629 # A string is valid JSON, make the difference between text
630 # and a plain JSON string.
631 # Content-Type is a good indicator of intent from user
632 elif content_type and content_type.startswith("text/"):
633 request.set_text_body(content)
634 else:
635 try:
636 request.set_json_body(content)
637 except TypeError:
638 request.data = content
640 if form_content:
641 request.set_formdata_body(form_content)
642 elif stream_content:
643 request.set_streamed_data_body(stream_content)
645 return request
647 def format_url(self, url_template: str, **kwargs: Any) -> str:
648 """Format request URL with the client base URL, unless the
649 supplied URL is already absolute.
651 Note that both the base url and the template url can contain query parameters.
653 :param str url_template: The request URL to be formatted if necessary.
654 :rtype: str
655 :return: The formatted URL.
656 """
657 url = _format_url_section(url_template, **kwargs)
658 if url:
659 parsed = urlparse(url)
660 if not parsed.scheme or not parsed.netloc:
661 url = url.lstrip("/")
662 try:
663 base = self._base_url.format(**kwargs).rstrip("/")
664 except KeyError as key:
665 err_msg = "The value provided for the url part {} was incorrect, and resulted in an invalid url"
666 raise ValueError(err_msg.format(key.args[0])) from key
668 url = _urljoin(base, url)
669 else:
670 url = self._base_url.format(**kwargs)
671 return url
673 def get(
674 self,
675 url: str,
676 params: Optional[Dict[str, str]] = None,
677 headers: Optional[Dict[str, str]] = None,
678 content: Any = None,
679 form_content: Optional[Dict[str, Any]] = None,
680 ) -> "HttpRequest":
681 """Create a GET request object.
683 :param str url: The request URL.
684 :param dict params: Request URL parameters.
685 :param dict headers: Headers
686 :param content: The body content
687 :type content: bytes or str or dict
688 :param dict form_content: Form content
689 :return: An HttpRequest object
690 :rtype: ~azure.core.pipeline.transport.HttpRequest
691 """
692 request = self._request("GET", url, params, headers, content, form_content, None)
693 request.method = "GET"
694 return request
696 def put(
697 self,
698 url: str,
699 params: Optional[Dict[str, str]] = None,
700 headers: Optional[Dict[str, str]] = None,
701 content: Any = None,
702 form_content: Optional[Dict[str, Any]] = None,
703 stream_content: Any = None,
704 ) -> HttpRequest:
705 """Create a PUT request object.
707 :param str url: The request URL.
708 :param dict params: Request URL parameters.
709 :param dict headers: Headers
710 :param content: The body content
711 :type content: bytes or str or dict
712 :param dict form_content: Form content
713 :param stream_content: The body content as a stream
714 :type stream_content: stream or generator or asyncgenerator
715 :return: An HttpRequest object
716 :rtype: ~azure.core.pipeline.transport.HttpRequest
717 """
718 request = self._request("PUT", url, params, headers, content, form_content, stream_content)
719 return request
721 def post(
722 self,
723 url: str,
724 params: Optional[Dict[str, str]] = None,
725 headers: Optional[Dict[str, str]] = None,
726 content: Any = None,
727 form_content: Optional[Dict[str, Any]] = None,
728 stream_content: Any = None,
729 ) -> HttpRequest:
730 """Create a POST request object.
732 :param str url: The request URL.
733 :param dict params: Request URL parameters.
734 :param dict headers: Headers
735 :param content: The body content
736 :type content: bytes or str or dict
737 :param dict form_content: Form content
738 :param stream_content: The body content as a stream
739 :type stream_content: stream or generator or asyncgenerator
740 :return: An HttpRequest object
741 :rtype: ~azure.core.pipeline.transport.HttpRequest
742 """
743 request = self._request("POST", url, params, headers, content, form_content, stream_content)
744 return request
746 def head(
747 self,
748 url: str,
749 params: Optional[Dict[str, str]] = None,
750 headers: Optional[Dict[str, str]] = None,
751 content: Any = None,
752 form_content: Optional[Dict[str, Any]] = None,
753 stream_content: Any = None,
754 ) -> HttpRequest:
755 """Create a HEAD request object.
757 :param str url: The request URL.
758 :param dict params: Request URL parameters.
759 :param dict headers: Headers
760 :param content: The body content
761 :type content: bytes or str or dict
762 :param dict form_content: Form content
763 :param stream_content: The body content as a stream
764 :type stream_content: stream or generator or asyncgenerator
765 :return: An HttpRequest object
766 :rtype: ~azure.core.pipeline.transport.HttpRequest
767 """
768 request = self._request("HEAD", url, params, headers, content, form_content, stream_content)
769 return request
771 def patch(
772 self,
773 url: str,
774 params: Optional[Dict[str, str]] = None,
775 headers: Optional[Dict[str, str]] = None,
776 content: Any = None,
777 form_content: Optional[Dict[str, Any]] = None,
778 stream_content: Any = None,
779 ) -> HttpRequest:
780 """Create a PATCH request object.
782 :param str url: The request URL.
783 :param dict params: Request URL parameters.
784 :param dict headers: Headers
785 :param content: The body content
786 :type content: bytes or str or dict
787 :param dict form_content: Form content
788 :param stream_content: The body content as a stream
789 :type stream_content: stream or generator or asyncgenerator
790 :return: An HttpRequest object
791 :rtype: ~azure.core.pipeline.transport.HttpRequest
792 """
793 request = self._request("PATCH", url, params, headers, content, form_content, stream_content)
794 return request
796 def delete(
797 self,
798 url: str,
799 params: Optional[Dict[str, str]] = None,
800 headers: Optional[Dict[str, str]] = None,
801 content: Any = None,
802 form_content: Optional[Dict[str, Any]] = None,
803 ) -> HttpRequest:
804 """Create a DELETE request object.
806 :param str url: The request URL.
807 :param dict params: Request URL parameters.
808 :param dict headers: Headers
809 :param content: The body content
810 :type content: bytes or str or dict
811 :param dict form_content: Form content
812 :return: An HttpRequest object
813 :rtype: ~azure.core.pipeline.transport.HttpRequest
814 """
815 request = self._request("DELETE", url, params, headers, content, form_content, None)
816 return request
818 def merge(
819 self,
820 url: str,
821 params: Optional[Dict[str, str]] = None,
822 headers: Optional[Dict[str, str]] = None,
823 content: Any = None,
824 form_content: Optional[Dict[str, Any]] = None,
825 ) -> HttpRequest:
826 """Create a MERGE request object.
828 :param str url: The request URL.
829 :param dict params: Request URL parameters.
830 :param dict headers: Headers
831 :param content: The body content
832 :type content: bytes or str or dict
833 :param dict form_content: Form content
834 :return: An HttpRequest object
835 :rtype: ~azure.core.pipeline.transport.HttpRequest
836 """
837 request = self._request("MERGE", url, params, headers, content, form_content, None)
838 return request
840 def options(
841 self, # pylint: disable=unused-argument
842 url: str,
843 params: Optional[Dict[str, str]] = None,
844 headers: Optional[Dict[str, str]] = None,
845 *,
846 content: Optional[Union[bytes, str, Dict[Any, Any]]] = None,
847 form_content: Optional[Dict[Any, Any]] = None,
848 **kwargs: Any,
849 ) -> HttpRequest:
850 """Create a OPTIONS request object.
852 :param str url: The request URL.
853 :param dict params: Request URL parameters.
854 :param dict headers: Headers
855 :keyword content: The body content
856 :type content: bytes or str or dict
857 :keyword dict form_content: Form content
858 :return: An HttpRequest object
859 :rtype: ~azure.core.pipeline.transport.HttpRequest
860 """
861 request = self._request("OPTIONS", url, params, headers, content, form_content, None)
862 return request