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 collections.abc import AsyncIterator 
    29from typing import ( 
    30    AsyncIterator as AsyncIteratorType, 
    31    TypeVar, 
    32    Generic, 
    33    Any, 
    34    AsyncContextManager, 
    35    Optional, 
    36    Type, 
    37    TYPE_CHECKING, 
    38) 
    39from types import TracebackType 
    40 
    41from ._base import _HttpResponseBase, _HttpClientTransportResponse, HttpRequest 
    42from ...utils._pipeline_transport_rest_shared_async import _PartGenerator 
    43 
    44 
    45AsyncHTTPResponseType = TypeVar("AsyncHTTPResponseType") 
    46HTTPResponseType = TypeVar("HTTPResponseType") 
    47HTTPRequestType = TypeVar("HTTPRequestType") 
    48 
    49if TYPE_CHECKING: 
    50    # We need a transport to define a pipeline, this "if" avoid a circular import 
    51    from .._base_async import AsyncPipeline 
    52 
    53 
    54class _ResponseStopIteration(Exception): 
    55    pass 
    56 
    57 
    58def _iterate_response_content(iterator): 
    59    """To avoid the following error from Python: 
    60    > TypeError: StopIteration interacts badly with generators and cannot be raised into a Future 
    61 
    62    :param iterator: An iterator 
    63    :type iterator: iterator 
    64    :return: The next item in the iterator 
    65    :rtype: any 
    66    """ 
    67    try: 
    68        return next(iterator) 
    69    except StopIteration: 
    70        raise _ResponseStopIteration()  # pylint: disable=raise-missing-from 
    71 
    72 
    73class AsyncHttpResponse(_HttpResponseBase, AsyncContextManager["AsyncHttpResponse"]): 
    74    """An AsyncHttpResponse ABC. 
    75 
    76    Allows for the asynchronous streaming of data from the response. 
    77    """ 
    78 
    79    def stream_download( 
    80        self, 
    81        pipeline: AsyncPipeline[HttpRequest, "AsyncHttpResponse"], 
    82        *, 
    83        decompress: bool = True, 
    84        **kwargs: Any, 
    85    ) -> AsyncIteratorType[bytes]: 
    86        """Generator for streaming response body data. 
    87 
    88        Should be implemented by sub-classes if streaming download 
    89        is supported. Will return an asynchronous generator. 
    90 
    91        :param pipeline: The pipeline object 
    92        :type pipeline: azure.core.pipeline.Pipeline 
    93        :keyword bool decompress: If True which is default, will attempt to decode the body based 
    94            on the *content-encoding* header. 
    95        :return: An async iterator of bytes 
    96        :rtype: AsyncIterator[bytes] 
    97        """ 
    98        raise NotImplementedError("stream_download is not implemented.") 
    99 
    100    def parts(self) -> AsyncIterator["AsyncHttpResponse"]: 
    101        """Assuming the content-type is multipart/mixed, will return the parts as an async iterator. 
    102 
    103        :return: An async iterator of the parts 
    104        :rtype: AsyncIterator 
    105        :raises ValueError: If the content is not multipart/mixed 
    106        """ 
    107        if not self.content_type or not self.content_type.startswith("multipart/mixed"): 
    108            raise ValueError("You can't get parts if the response is not multipart/mixed") 
    109 
    110        return _PartGenerator(self, default_http_response_type=AsyncHttpClientTransportResponse) 
    111 
    112    async def __aexit__( 
    113        self, 
    114        exc_type: Optional[Type[BaseException]] = None, 
    115        exc_value: Optional[BaseException] = None, 
    116        traceback: Optional[TracebackType] = None, 
    117    ) -> None: 
    118        return None 
    119 
    120 
    121class AsyncHttpClientTransportResponse(_HttpClientTransportResponse, AsyncHttpResponse): 
    122    """Create a HTTPResponse from an http.client response. 
    123 
    124    Body will NOT be read by the constructor. Call "body()" to load the body in memory if necessary. 
    125 
    126    :param HttpRequest request: The request. 
    127    :param httpclient_response: The object returned from an HTTP(S)Connection from http.client 
    128    """ 
    129 
    130 
    131class AsyncHttpTransport( 
    132    AsyncContextManager["AsyncHttpTransport"], 
    133    abc.ABC, 
    134    Generic[HTTPRequestType, AsyncHTTPResponseType], 
    135): 
    136    """An http sender ABC.""" 
    137 
    138    @abc.abstractmethod 
    139    async def send(self, request: HTTPRequestType, **kwargs: Any) -> AsyncHTTPResponseType: 
    140        """Send the request using this HTTP sender. 
    141 
    142        :param request: The request object. Exact type can be inferred from the pipeline. 
    143        :type request: any 
    144        :return: The response object. Exact type can be inferred from the pipeline. 
    145        :rtype: any 
    146        """ 
    147 
    148    @abc.abstractmethod 
    149    async def open(self) -> None: 
    150        """Assign new session if one does not already exist.""" 
    151 
    152    @abc.abstractmethod 
    153    async def close(self) -> None: 
    154        """Close the session if it is not externally owned.""" 
    155 
    156    async def sleep(self, duration: float) -> None: 
    157        """Sleep for the specified duration. 
    158 
    159        You should always ask the transport to sleep, and not call directly 
    160        the stdlib. This is mostly important in async, as the transport 
    161        may not use asyncio but other implementation like trio and they their own 
    162        way to sleep, but to keep design 
    163        consistent, it's cleaner to always ask the transport to sleep and let the transport 
    164        implementor decide how to do it. 
    165        By default, this method will use "asyncio", and don't need to be overridden 
    166        if your transport does too. 
    167 
    168        :param float duration: The number of seconds to sleep. 
    169        """ 
    170        import asyncio  # pylint: disable=do-not-import-asyncio 
    171 
    172        await asyncio.sleep(duration)