o
    V-                     @   s  d Z ddlmZ ddlmZ ddlmZ ddlZddlZddlZddlZddl	Z	ddl
Z
ddlZddlmZ zejZW n eyF   eZY nw dZG dd	 d	eZG d
d deZG dd deZG dd deZG dd deZdddddeddfddZdd Zdd ZdS )z!Implementation of retrying logic.    )absolute_import)division)unicode_literalsN)
exceptions  c                   @   s   e Zd ZdZdd ZdS )RetryerStatez+Object that holds the state of the retryer.c                 C   s   || _ || _|| _dS )a  Initializer for RetryerState.

    Args:
      retrial: int, the retry attempt we are currently at.
      time_passed_ms: int, number of ms that passed since we started retryer.
      time_to_wait_ms: int, number of ms to wait for the until next trial.
          If this number is -1, it means the iterable item that specifies the
          next sleep value has raised StopIteration.
    N)retrialtime_passed_mstime_to_wait_ms)selfr   r	   r
    r   ;/tmp/google-cloud-sdk/lib/googlecloudsdk/core/util/retry.py__init__-   s   

zRetryerState.__init__N)__name__
__module____qualname____doc__r   r   r   r   r   r   *   s    r   c                       s(   e Zd ZdZ fddZdd Z  ZS )RetryExceptionz#Raised to stop retrials on failure.c                    s&   || _ || _|| _tt| | d S N)messagelast_resultstatesuperr   r   )r   r   r   r   	__class__r   r   r   ?   s   zRetryException.__init__c                 C   s    dj | j| jj| jj| jjdS )Nzvlast_result={last_result}, last_retrial={last_retrial}, time_passed_ms={time_passed_ms},time_to_wait={time_to_wait_ms})r   last_retrialr	   r
   )formatr   r   r   r	   r
   )r   r   r   r   __str__E   s   zRetryException.__str__)r   r   r   r   r   r   __classcell__r   r   r   r   r   <   s    r   c                   @      e Zd ZdZdS )WaitExceptionz Raised when timeout was reached.Nr   r   r   r   r   r   r   r   r    O       r    c                   @   r   )MaxRetrialsExceptionz&Raised when too many retrials reached.Nr!   r   r   r   r   r#   S   r"   r#   c                   @   sR   e Zd ZdZdddeddfddZdd Zdd Z		dd	d
Z		dddZ	dS )Retryerz5Retries a function based on specified retry strategy.Nc                 C   s(   || _ || _|| _|| _|| _|| _dS )a>  Initializer for Retryer.

    Args:
      max_retrials: int, max number of retrials before raising RetryException.
      max_wait_ms: int, number of ms to wait before raising
      exponential_sleep_multiplier: float, The exponential factor to use on
          subsequent retries.
      jitter_ms: int, random [0, jitter_ms] additional value to wait.
      status_update_func: func(result, state) called right after each trial.
      wait_ceiling_ms: int, maximum wait time between retries, regardless of
          modifiers added like exponential multiplier or jitter.
    N)_max_retrials_max_wait_ms_exponential_sleep_multiplier
_jitter_ms_status_update_func_wait_ceiling_ms)r   max_retrialsmax_wait_msexponential_sleep_multiplier	jitter_msstatus_update_funcwait_ceiling_msr   r   r   r   Z   s   
zRetryer.__init__c                 C   sR   | j d ur| j |jkrtd||| jd ur%|j|j | jkr'td||d S d S )NReachedTimeout)r%   r   r#   r&   r	   r
   r    )r   resultr   r   r   r   _RaiseIfStopq   s   
zRetryer._RaiseIfStopc                 C   sx   |}|r:| j r#d}| j dkr|t|| | j kr|}n|| j | 9 }| jr/|t | j 7 }| jr8t|| j}|S dS )a  Get time to wait after applying modifyers.

    Apply the exponential sleep multiplyer, jitter and ceiling limiting to the
    base sleep time.

    Args:
      last_retrial: int, which retry attempt we just tried. First try this is 0.
      sleep_ms: int, how long to wait between the current trials.

    Returns:
      int, ms to wait before trying next attempt with all waiting logic applied.
    l    0jyg      ?r   )r'   mathlogr(   randomr*   min)r   r   sleep_mswait_time_mshundred_years_msr   r   r   _GetTimeToWaitx   s    zRetryer._GetTimeToWaitc                    s    dur nd durni  fdd}du r!dd }nfdd}|}| j |||d	\}	}
|
r@tj|
d
 |
d d |	S )aV  Retries the function if an exception occurs.

    Args:
      func: The function to call and retry.
      args: a sequence of positional arguments to be passed to func.
      kwargs: a dictionary of positional arguments to be passed to func.
      should_retry_if: func(exc_type, exc_value, exc_traceback, state) that
          returns True or False.
      sleep_ms: int or iterable for how long to wait between trials.

    Returns:
      Whatever the function returns.

    Raises:
      RetryException, WaitException: if function is retries too many times,
        or time limit is reached.
    Nr   c                      s,   z
 i d fW S    d t  f Y S r   )sysexc_infor   )argsfunckwargsr   r   TryFunc   s   z)Retryer.RetryOnException.<locals>.TryFuncc                 S   s   | d d uS )N   r   xsr   r   r   <lambda>   s    z*Retryer.RetryOnException.<locals>.<lambda>c                    s.   | d }|d u r
dS  |d |d |d |S )NrC   Fr      r   )try_func_resultr   r>   should_retry_ifr   r   ShouldRetryFunc   s   z1Retryer.RetryOnException.<locals>.ShouldRetryFunc)rK   r9   rC   rH   tb)RetryOnResultr   reraise)r   r@   r?   rA   rK   r9   rB   should_retryrL   r3   r>   r   )r?   r@   rA   rK   r   RetryOnException   s   

zRetryer.RetryOnExceptionc                    s  |dur|nd}|dur|ni }t  }d}t r }n fdd}t|tjr-t|}	nt|}		 ||i |}
t  | }zt|	}W n t	yP   d}Y nw | 
||}t|||}||
|sd|
S |dkrntd|
|| jrw| |
| | |
| t| |d	7 }q3)
ao  Retries the function if the given condition is satisfied.

    Args:
      func: The function to call and retry.
      args: a sequence of arguments to be passed to func.
      kwargs: a dictionary of positional arguments to be passed to func.
      should_retry_if: result to retry on or func(result, RetryerState) that
          returns True or False if we should retry or not.
      sleep_ms: int or iterable, for how long to wait between trials.

    Returns:
      Whatever the function returns.

    Raises:
      MaxRetrialsException: function retried too many times.
      WaitException: time limit is reached.
    Nr   r   c                    s   |  kS r   r   rD   rJ   r   r   rG      s    z'Retryer.RetryOnResult.<locals>.<lambda>TzSleep iteration stoprC   )_GetCurrentTimeMscallable
isinstancecollections_abcIterableiter	itertoolsrepeatnextStopIterationr<   r   r#   r)   r4   _SleepMs)r   r@   r?   rA   rK   r9   start_time_msr   rQ   	sleep_genr3   r	   sleep_from_genr
   r   r   rJ   r   rO      s<   



zRetryer.RetryOnResult)NNNN)
r   r   r   r   _DEFAULT_JITTER_MSr   r4   r<   rR   rO   r   r   r   r   r$   W   s    
%
/r$   c           	   
      sJ   du rt jt dS t  fdd}|S )a  A decorator to retry on exceptions.

  Args:
    f: a function to run possibly multiple times
    max_retrials: int, max number of retrials before raising RetryException.
    max_wait_ms: int, number of ms to wait before raising
    sleep_ms: int or iterable, for how long to wait between trials.
    exponential_sleep_multiplier: float, The exponential factor to use on
        subsequent retries.
    jitter_ms: int, random [0, jitter_ms] additional value to wait.
    status_update_func: func(result, state) called right after each trail.
    should_retry_if: func(exc_type, exc_value, exc_traceback, state) that
        returns True or False.

  Returns:
    A version of f that is executed potentially multiple times and that
    yields the first returned value or the last exception raised.
  N)r-   r.   r+   r,   rK   r9   r/   c               
      sp   t  d}z|j| |dW S  ty7 } z|jd }tj|d |d d W Y d }~d S d }~ww )N)r+   r,   r-   r.   r/   )r?   rA   rK   r9   rC   rH   rM   )r$   rR   r#   r   r   rP   )r?   rA   retryermre
to_reraiser-   fr.   r+   r,   rK   r9   r/   r   r   DecoratedFunction*  s"   

$z+RetryOnException.<locals>.DecoratedFunction)	functoolspartialrR   wraps)	rg   r+   r,   r9   r-   r.   r/   rK   rh   r   rf   r   rR     s   
rR   c                   C   s   t t d S )Nr   )inttimer   r   r   r   rT   =  s   rT   c                 C   s   t | d  d S )Ng     @@)rm   sleep)r
   r   r   r   r^   A  s   r^   )r   
__future__r   r   r   collectionsri   rZ   r5   r7   r=   rm   googlecloudsdk.corer   abcrW   AttributeErrorrb   objectr   	Exceptionr   r    r#   r$   rR   rT   r^   r   r   r   r   <module>   s@   
 /
8