o
    i:9                  
   @  sN  d Z ddlmZ ddlmZ ddlmZmZmZm	Z	m
Z
mZmZ zddlmZmZmZmZ W n ey? Z zededZ[ww ddlmZmZ dd	lmZmZ dd
lmZ ddlmZmZmZ ddlm Z  erddl!m"Z" ddl#m"Z$ ddl%m&Z& ddl'm(Z( g dZ)G dd de ddZ*G dd deZ+G dd deZ,	d"d#d d!Z-dS )$au  Retries utilities based on tenacity, especially for HTTP requests.

This module provides HTTP transport wrappers and wait strategies that integrate with
the tenacity library to add retry capabilities to HTTP requests. The transports can be
used with HTTP clients that support custom transports (such as httpx), while the wait
strategies can be used with any tenacity retry decorator.

The module includes:
- TenacityTransport: Synchronous HTTP transport with retry capabilities
- AsyncTenacityTransport: Asynchronous HTTP transport with retry capabilities
- wait_retry_after: Wait strategy that respects HTTP Retry-After headers
    )annotations)TracebackType)AsyncBaseTransportAsyncHTTPTransportBaseTransportHTTPStatusErrorHTTPTransportRequestResponse)RetryCallState
RetryErrorretrywait_exponentialu   Please install `tenacity` to use the retries utilities, you can use the `retries` optional group — `pip install "pydantic-ai-slim[retries]"`N)	AwaitableCallable)datetimetimezone)parsedate_to_datetime)TYPE_CHECKINGAnycast)	TypedDict)
RetryBaseT)	StopBaseT)	WaitBaseT)RetryConfigTenacityTransportAsyncTenacityTransportwait_retry_afterc                   @  st   e Zd ZU dZded< 	 ded< 	 ded< 	 ded	< 	 d
ed< 	 d
ed< 	 ded< 	 ded< 	 ded< 	 ded< dS )r   ae  The configuration for tenacity-based retrying.

    These are precisely the arguments to the tenacity `retry` decorator, and they are generally
    used internally by passing them to that decorator via `@retry(**config)` or similar.

    All fields are optional, and if not provided, the default values from the `tenacity.retry` decorator will be used.
    z/Callable[[int | float], None | Awaitable[None]]sleepr   stopr   waitzSyncRetryBaseT | RetryBaseTr   z2Callable[[RetryCallState], None | Awaitable[None]]beforeafterz9Callable[[RetryCallState], None | Awaitable[None]] | Nonebefore_sleepboolreraiseztype[RetryError]retry_error_clsz7Callable[[RetryCallState], Any | Awaitable[Any]] | Noneretry_error_callbackN)__name__
__module____qualname____doc____annotations__ r.   r.   Y/var/www/html/karishye-ai-python/venv/lib/python3.10/site-packages/pydantic_ai/retries.pyr   4   s,   
 r   F)totalc                   @  P   e Zd ZdZ		ddd	d
Zd ddZd!ddZ			d"d#ddZd$ddZdS )%r   ac  Synchronous HTTP transport with tenacity-based retry functionality.

    This transport wraps another BaseTransport and adds retry capabilities using the tenacity library.
    It can be configured to retry requests based on various conditions such as specific exception types,
    response status codes, or custom validation logic.

    The transport works by intercepting HTTP requests and responses, allowing the tenacity controller
    to determine when and how to retry failed requests. The validate_response function can be used
    to convert HTTP responses into exceptions that trigger retries.

    Args:
        wrapped: The underlying transport to wrap and add retry functionality to.
        config: The arguments to use for the tenacity `retry` decorator, including retry conditions,
            wait strategy, stop conditions, etc. See the tenacity docs for more info.
        validate_response: Optional callable that takes a Response and can raise an exception
            to be handled by the controller if the response should trigger a retry.
            Common use case is to raise exceptions for certain HTTP status codes.
            If None, no response validation is performed.

    Example:
        ```python
        from httpx import Client, HTTPStatusError, HTTPTransport
        from tenacity import retry_if_exception_type, stop_after_attempt

        from pydantic_ai.retries import RetryConfig, TenacityTransport, wait_retry_after

        transport = TenacityTransport(
            RetryConfig(
                retry=retry_if_exception_type(HTTPStatusError),
                wait=wait_retry_after(max_wait=300),
                stop=stop_after_attempt(5),
                reraise=True
            ),
            HTTPTransport(),
            validate_response=lambda r: r.raise_for_status()
        )
        client = Client(transport=transport)
        ```
    Nconfigr   wrappedBaseTransport | Nonevalidate_response Callable[[Response], Any] | Nonec                 C     || _ |pt | _|| _d S N)r2   r   r3   r5   selfr2   r3   r5   r.   r.   r/   __init__      
zTenacityTransport.__init__requestr	   returnr
   c                   s&   t di  jd	 fdd}||S )
a]  Handle an HTTP request with retry logic.

        Args:
            request: The HTTP request to handle.

        Returns:
            The HTTP response.

        Raises:
            RuntimeError: If the retry controller did not make any attempts.
            Exception: Any exception raised by the wrapped transport or validation function.
        reqr	   r>   r
   c                   sF    j | }| |_ jr!z | W |S  ty    |   w |S r8   )r3   handle_requestr=   r5   	Exceptioncloser?   responser:   r.   r/   r@      s   z8TenacityTransport.handle_request.<locals>.handle_requestNr.   r?   r	   r>   r
   r   r2   )r:   r=   r@   r.   rE   r/   r@      s   z TenacityTransport.handle_requestc                 C  s   | j   | S r8   )r3   	__enter__rE   r.   r.   r/   rH      s   
zTenacityTransport.__enter__exc_typetype[BaseException] | None	exc_valueBaseException | None	tracebackTracebackType | NoneNonec                 C  s   | j ||| d S r8   )r3   __exit__r:   rI   rK   rM   r.   r.   r/   rP      s   zTenacityTransport.__exit__c                 C  s   | j   d S r8   )r3   rB   rE   r.   r.   r/   rB      s   zTenacityTransport.closeNN)r2   r   r3   r4   r5   r6   r=   r	   r>   r
   )r>   r   NNNrI   rJ   rK   rL   rM   rN   r>   rO   r>   rO   )	r)   r*   r+   r,   r;   r@   rH   rP   rB   r.   r.   r.   r/   r   u   s    +


r   c                   @  r1   )%r   aW  Asynchronous HTTP transport with tenacity-based retry functionality.

    This transport wraps another AsyncBaseTransport and adds retry capabilities using the tenacity library.
    It can be configured to retry requests based on various conditions such as specific exception types,
    response status codes, or custom validation logic.

    The transport works by intercepting HTTP requests and responses, allowing the tenacity controller
    to determine when and how to retry failed requests. The validate_response function can be used
    to convert HTTP responses into exceptions that trigger retries.

    Args:
        wrapped: The underlying async transport to wrap and add retry functionality to.
        config: The arguments to use for the tenacity `retry` decorator, including retry conditions,
            wait strategy, stop conditions, etc. See the tenacity docs for more info.
        validate_response: Optional callable that takes a Response and can raise an exception
            to be handled by the controller if the response should trigger a retry.
            Common use case is to raise exceptions for certain HTTP status codes.
            If None, no response validation is performed.

    Example:
        ```python
        from httpx import AsyncClient, HTTPStatusError
        from tenacity import retry_if_exception_type, stop_after_attempt

        from pydantic_ai.retries import AsyncTenacityTransport, RetryConfig, wait_retry_after

        transport = AsyncTenacityTransport(
            RetryConfig(
                retry=retry_if_exception_type(HTTPStatusError),
                wait=wait_retry_after(max_wait=300),
                stop=stop_after_attempt(5),
                reraise=True
            ),
            validate_response=lambda r: r.raise_for_status()
        )
        client = AsyncClient(transport=transport)
        ```
    Nr2   r   r3   AsyncBaseTransport | Noner5   r6   c                 C  r7   r8   )r2   r   r3   r5   r9   r.   r.   r/   r;      r<   zAsyncTenacityTransport.__init__r=   r	   r>   r
   c                   s.   t di  jd	 fdd}||I dH S )
ac  Handle an async HTTP request with retry logic.

        Args:
            request: The HTTP request to handle.

        Returns:
            The HTTP response.

        Raises:
            RuntimeError: If the retry controller did not make any attempts.
            Exception: Any exception raised by the wrapped transport or validation function.
        r?   r	   r>   r
   c                   sT    j | I d H }| |_ jr(z | W |S  ty'   | I d H   w |S r8   )r3   handle_async_requestr=   r5   rA   acloserC   rE   r.   r/   rX     s   zIAsyncTenacityTransport.handle_async_request.<locals>.handle_async_requestNr.   rF   rG   )r:   r=   rX   r.   rE   r/   rX   	  s   z+AsyncTenacityTransport.handle_async_requestc                   s   | j  I d H  | S r8   )r3   
__aenter__rE   r.   r.   r/   rZ   (  s   z!AsyncTenacityTransport.__aenter__rI   rJ   rK   rL   rM   rN   rO   c                   s   | j |||I d H  d S r8   )r3   	__aexit__rQ   r.   r.   r/   r[   ,  s   z AsyncTenacityTransport.__aexit__c                   s   | j  I d H  d S r8   )r3   rY   rE   r.   r.   r/   rY   4  s   zAsyncTenacityTransport.acloserR   )r2   r   r3   rW   r5   r6   rS   )r>   r   rT   rU   rV   )	r)   r*   r+   r,   r;   rX   rZ   r[   rY   r.   r.   r.   r/   r      s    *


r   ,  fallback_strategy(Callable[[RetryCallState], float] | Nonemax_waitfloatr>   !Callable[[RetryCallState], float]c                   s(    du r
t ddd d fd	d
}|S )a  Create a tenacity-compatible wait strategy that respects HTTP Retry-After headers.

    This wait strategy checks if the exception contains an HTTPStatusError with a
    Retry-After header, and if so, waits for the time specified in the header.
    If no header is present or parsing fails, it falls back to the provided strategy.

    The Retry-After header can be in two formats:
    - An integer representing seconds to wait
    - An HTTP date string representing when to retry

    Args:
        fallback_strategy: Wait strategy to use when no Retry-After header is present
                          or parsing fails. Defaults to exponential backoff with max 60s.
        max_wait: Maximum time to wait in seconds, regardless of header value.
                 Defaults to 300 (5 minutes).

    Returns:
        A wait function that can be used with tenacity retry decorators.

    Example:
        ```python
        from httpx import AsyncClient, HTTPStatusError
        from tenacity import retry_if_exception_type, stop_after_attempt

        from pydantic_ai.retries import AsyncTenacityTransport, RetryConfig, wait_retry_after

        transport = AsyncTenacityTransport(
            RetryConfig(
                retry=retry_if_exception_type(HTTPStatusError),
                wait=wait_retry_after(max_wait=120),
                stop=stop_after_attempt(5),
                reraise=True
            ),
            validate_response=lambda r: r.raise_for_status()
        )
        client = AsyncClient(transport=transport)
        ```
    N   <   )
multipliermaxstater   r>   r`   c                   s   | j r| j  nd }t|trk|jjd}|rkzt|}tt	|W S  t
yj   z(ttt|}t|ts:J ttj}||  }|dkrRt|W  Y S W n t
ttfy_   Y nw Y  | S Y  | S w  | S )Nzretry-afterr   )outcome	exception
isinstancer   rD   headersgetintminr`   
ValueErrorr   r   r   nowr   utctotal_seconds	TypeErrorAssertionError)rf   excretry_afterwait_seconds
retry_timero   r]   r_   r.   r/   	wait_funcd  s2   
z#wait_retry_after.<locals>.wait_func)rf   r   r>   r`   )r   )r]   r_   ry   r.   rx   r/   r   8  s   )r   )Nr\   )r]   r^   r_   r`   r>   ra   ).r,   
__future__r   typesr   httpxr   r   r   r   r   r	   r
   tenacityr   r   r   r   ImportError_import_errorcollections.abcr   r   r   r   email.utilsr   typingr   r   r   typing_extensionsr   tenacity.asyncio.retryr   tenacity.retrySyncRetryBaseTtenacity.stopr   tenacity.waitr   __all__r   r   r   r   r.   r.   r.   r/   <module>   s<    $
Abb