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 logging 
    28from collections.abc import Iterable 
    29from typing import TypeVar, Generic, Optional, Any 
    30from .configuration import Configuration 
    31from .pipeline import Pipeline 
    32from .pipeline.transport._base import PipelineClientBase 
    33from .pipeline.transport import HttpTransport 
    34from .pipeline.policies import ( 
    35    ContentDecodePolicy, 
    36    DistributedTracingPolicy, 
    37    HttpLoggingPolicy, 
    38    RequestIdPolicy, 
    39    RetryPolicy, 
    40    SensitiveHeaderCleanupPolicy, 
    41) 
    42 
    43HTTPResponseType = TypeVar("HTTPResponseType") 
    44HTTPRequestType = TypeVar("HTTPRequestType") 
    45 
    46_LOGGER = logging.getLogger(__name__) 
    47 
    48 
    49class PipelineClient(PipelineClientBase, Generic[HTTPRequestType, HTTPResponseType]): 
    50    """Service client core methods. 
    51 
    52    Builds a Pipeline client. 
    53 
    54    :param str base_url: URL for the request. 
    55    :keyword ~azure.core.configuration.Configuration config: If omitted, the standard configuration is used. 
    56    :keyword Pipeline pipeline: If omitted, a Pipeline object is created and returned. 
    57    :keyword list[HTTPPolicy] policies: If omitted, the standard policies of the configuration object is used. 
    58    :keyword per_call_policies: If specified, the policies will be added into the policy list before RetryPolicy 
    59    :paramtype per_call_policies: Union[HTTPPolicy, SansIOHTTPPolicy, list[HTTPPolicy], list[SansIOHTTPPolicy]] 
    60    :keyword per_retry_policies: If specified, the policies will be added into the policy list after RetryPolicy 
    61    :paramtype per_retry_policies: Union[HTTPPolicy, SansIOHTTPPolicy, list[HTTPPolicy], list[SansIOHTTPPolicy]] 
    62    :keyword HttpTransport transport: If omitted, RequestsTransport is used for synchronous transport. 
    63    :return: A pipeline object. 
    64    :rtype: ~azure.core.pipeline.Pipeline 
    65 
    66    .. admonition:: Example: 
    67 
    68        .. literalinclude:: ../samples/test_example_sync.py 
    69            :start-after: [START build_pipeline_client] 
    70            :end-before: [END build_pipeline_client] 
    71            :language: python 
    72            :dedent: 4 
    73            :caption: Builds the pipeline client. 
    74    """ 
    75 
    76    def __init__( 
    77        self, 
    78        base_url: str, 
    79        *, 
    80        pipeline: Optional[Pipeline[HTTPRequestType, HTTPResponseType]] = None, 
    81        config: Optional[Configuration[HTTPRequestType, HTTPResponseType]] = None, 
    82        **kwargs: Any, 
    83    ): 
    84        super(PipelineClient, self).__init__(base_url) 
    85        self._config: Configuration[HTTPRequestType, HTTPResponseType] = config or Configuration(**kwargs) 
    86        self._base_url = base_url 
    87 
    88        self._pipeline = pipeline or self._build_pipeline(self._config, **kwargs) 
    89 
    90    def __enter__(self) -> PipelineClient[HTTPRequestType, HTTPResponseType]: 
    91        self._pipeline.__enter__() 
    92        return self 
    93 
    94    def __exit__(self, *exc_details: Any) -> None: 
    95        self._pipeline.__exit__(*exc_details) 
    96 
    97    def close(self) -> None: 
    98        self.__exit__() 
    99 
    100    def _build_pipeline( 
    101        self, 
    102        config: Configuration[HTTPRequestType, HTTPResponseType], 
    103        *, 
    104        transport: Optional[HttpTransport[HTTPRequestType, HTTPResponseType]] = None, 
    105        policies=None, 
    106        per_call_policies=None, 
    107        per_retry_policies=None, 
    108        **kwargs, 
    109    ) -> Pipeline[HTTPRequestType, HTTPResponseType]: 
    110        per_call_policies = per_call_policies or [] 
    111        per_retry_policies = per_retry_policies or [] 
    112 
    113        if policies is None:  # [] is a valid policy list 
    114            policies = [ 
    115                config.request_id_policy or RequestIdPolicy(**kwargs), 
    116                config.headers_policy, 
    117                config.user_agent_policy, 
    118                config.proxy_policy, 
    119                ContentDecodePolicy(**kwargs), 
    120            ] 
    121            if isinstance(per_call_policies, Iterable): 
    122                policies.extend(per_call_policies) 
    123            else: 
    124                policies.append(per_call_policies) 
    125 
    126            policies.extend( 
    127                [ 
    128                    config.redirect_policy, 
    129                    config.retry_policy, 
    130                    config.authentication_policy, 
    131                    config.custom_hook_policy, 
    132                ] 
    133            ) 
    134            if isinstance(per_retry_policies, Iterable): 
    135                policies.extend(per_retry_policies) 
    136            else: 
    137                policies.append(per_retry_policies) 
    138 
    139            policies.extend( 
    140                [ 
    141                    config.logging_policy, 
    142                    DistributedTracingPolicy(**kwargs), 
    143                    (SensitiveHeaderCleanupPolicy(**kwargs) if config.redirect_policy else None), 
    144                    config.http_logging_policy or HttpLoggingPolicy(**kwargs), 
    145                ] 
    146            ) 
    147        else: 
    148            if isinstance(per_call_policies, Iterable): 
    149                per_call_policies_list = list(per_call_policies) 
    150            else: 
    151                per_call_policies_list = [per_call_policies] 
    152            per_call_policies_list.extend(policies) 
    153            policies = per_call_policies_list 
    154 
    155            if isinstance(per_retry_policies, Iterable): 
    156                per_retry_policies_list = list(per_retry_policies) 
    157            else: 
    158                per_retry_policies_list = [per_retry_policies] 
    159            if len(per_retry_policies_list) > 0: 
    160                index_of_retry = -1 
    161                for index, policy in enumerate(policies): 
    162                    if isinstance(policy, RetryPolicy): 
    163                        index_of_retry = index 
    164                if index_of_retry == -1: 
    165                    raise ValueError( 
    166                        "Failed to add per_retry_policies; no RetryPolicy found in the supplied list of policies. " 
    167                    ) 
    168                policies_1 = policies[: index_of_retry + 1] 
    169                policies_2 = policies[index_of_retry + 1 :] 
    170                policies_1.extend(per_retry_policies_list) 
    171                policies_1.extend(policies_2) 
    172                policies = policies_1 
    173 
    174        if transport is None: 
    175            # Use private import for better typing, mypy and pyright don't like PEP562 
    176            from .pipeline.transport._requests_basic import RequestsTransport 
    177 
    178            transport = RequestsTransport(**kwargs) 
    179 
    180        return Pipeline(transport, policies) 
    181 
    182    def send_request(self, request: HTTPRequestType, *, stream: bool = False, **kwargs: Any) -> HTTPResponseType: 
    183        """Method that runs the network request through the client's chained policies. 
    184 
    185        >>> from azure.core.rest import HttpRequest 
    186        >>> request = HttpRequest('GET', 'http://www.example.com') 
    187        <HttpRequest [GET], url: 'http://www.example.com'> 
    188        >>> response = client.send_request(request) 
    189        <HttpResponse: 200 OK> 
    190 
    191        :param request: The network request you want to make. Required. 
    192        :type request: ~azure.core.rest.HttpRequest 
    193        :keyword bool stream: Whether the response payload will be streamed. Defaults to False. 
    194        :return: The response of your network call. Does not do error handling on your response. 
    195        :rtype: ~azure.core.rest.HttpResponse 
    196        """ 
    197        return_pipeline_response = kwargs.pop("_return_pipeline_response", False) 
    198        pipeline_response = self._pipeline.run(request, stream=stream, **kwargs) 
    199        if return_pipeline_response: 
    200            return pipeline_response  # type: ignore  # This is a private API we don't want to type in signature 
    201        return pipeline_response.http_response