
    V-                     :   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 	 ej                  ZdZ G d d	e      Z G d
 de      Z G d de      Z G d de      Z G d de      ZdddddeddfdZd Zd Zy# e$ r eZY Uw xY w)z!Implementation of retrying logic.    )absolute_import)division)unicode_literalsN)
exceptions  c                       e Zd ZdZd Zy)RetryerStatez+Object that holds the state of the retryer.c                 .    || _         || _        || _        y)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   s       %lib/googlecloudsdk/core/util/retry.py__init__zRetryerState.__init__-   s     DL(D*D    N)__name__
__module____qualname____doc__r    r   r   r	   r	   *   s
    3+r   r	   c                   (     e Zd ZdZ fdZd Z xZS )RetryExceptionz#Raised to stop retrials on failure.c                 V    || _         || _        || _        t        t        |   |       y N)messagelast_resultstatesuperr   r   )r   r   r   r   	__class__s       r   r   zRetryException.__init__?   s(    DL"DDJ	.$(1r   c                     dj                  | j                  | j                  j                  | j                  j                  | j                  j
                        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   s    r   __str__zRetryException.__str__E   sK    --3V ,,!ZZ//#zz88 $

 : :	 .4 .<=r   )r   r   r   r   r   r#   __classcell__)r   s   @r   r   r   <   s    +2=r   r   c                       e Zd ZdZy)WaitExceptionz Raised when timeout was reached.Nr   r   r   r   r   r   r   r&   r&   O   s    (r   r&   c                       e Zd ZdZy)MaxRetrialsExceptionz&Raised when too many retrials reached.Nr'   r   r   r   r)   r)   S   s    .r   r)   c                   H    e Zd ZdZdddeddfdZd Zd Z	 	 ddZ	 	 ddZ	y)	Retryerz5Retries a function based on specified retry strategy.Nc                 X    || _         || _        || _        || _        || _        || _        y)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_mss          r   r   zRetryer.__init__Z   s3      &D#D)ED&DO1D+Dr   c                     | j                   &| j                   |j                  k  rt        d||      | j                  4|j                  |j
                  z   | j                  kD  rt        d||      y y )NReachedTimeout)r-   r   r)   r.   r   r   r&   )r   resultr   s      r   _RaiseIfStopzRetryer._RaiseIfStopq   ss    %$*<*<*M FE::$			 5 5	58I8I	IIvu55 
J %r   c                 d   |}|r| j                   rLd}| j                   dkD  r)|t        j                  ||z  | j                         kD  r|}n|| j                   |z  z  }| j                  r$|t	        j                         | j                  z  z  }| j
                  rt        || j
                        }|S y)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logr0   randomr2   min)r   r!   sleep_mswait_time_mshundred_years_mss        r   _GetTimeToWaitzRetryer._GetTimeToWaitx   s     L		+	+ .--3txx|+..H0 90 *,
$<<L
L,	$//99			<)>)>?r   c                     ndni fd}d }nfd}|}| j                  |||      \  }	}
|
rt        j                  |
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.
    r   c                  T    	   i d fS #  d t        j                         fcY S xY wr   )sysexc_info)argsfunckwargss   r   TryFuncz)Retryer.RetryOnException.<locals>.TryFunc   s5    $T$V$d**$S\\^##s   	 'c                     | d   d uS )N   r   )xss     r   <lambda>z*Retryer.RetryOnException.<locals>.<lambda>   s    !A$d"2r   c                 <    | d   }|y |d   |d   |d   |      S )NrP   Fr      r   )try_func_resultr   rJ   should_retry_ifs      r   ShouldRetryFuncz1Retryer.RetryOnException.<locals>.ShouldRetryFunc   s3    "1%x{HQK!eLLr   )rW   rC   rP   rU   tb)RetryOnResultr   reraise)r   rL   rK   rM   rW   rC   rN   should_retryrX   r<   rJ   s    ````      r   RetryOnExceptionzRetryer.RetryOnException   s~    ( #4D)VrF$ 2lM %l)) * BFH!!5Mr   c                 0   ||nd}||ni }t               }d}t              r}nfd}t        |t        j                        rt        |      }	nt        j                  |      }		  ||i |}
t               |z
  }	 t        |	      }| j                  ||      }t        |||      } ||
|      s|
S |dk(  rt        d|
|      | j                  r| j                  |
|       | j                  |
|       t        |       |dz  }# t        $ r d}Y xw xY w)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.
    r   r   c                     | k(  S r   r   )rQ   rR   rW   s     r   rS   z'Retryer.RetryOnResult.<locals>.<lambda>   s
    !"6r   zSleep iteration stoprP   )_GetCurrentTimeMscallable
isinstancecollections_abcIterableiter	itertoolsrepeatnextrF   StopIterationr	   r)   r1   r=   _SleepMs)r   rL   rK   rM   rW   rC   start_time_msr   r]   	sleep_genr<   r   sleep_from_genr   r   s       `          r   r[   zRetryer.RetryOnResult   s1   & #4D)VrF%'MG $l6l(O445x.i""8,i
T$V$f(*]:nGi --g~F7NODe&%(	B	"#965II		!	!  /
&lg) 
  s   ?D DD)NNNN)
r   r   r   r   _DEFAULT_JITTER_MSr   r=   rF   r^   r[   r   r   r   r+   r+   W   s@    ="&D,0<N"&,.6#J 6:6:-^ 37376r   r+   c           
            !t        j                  t              S t        j                          f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.
  )r5   r6   r3   r4   rW   rC   r7   c                      t        	      }	 |j                  | |
      S # t        $ r6}|j                  d   }t	        j
                  |d   |d          Y d }~y d }~ww xY w)N)r3   r4   r5   r6   r7   )rK   rM   rW   rC   rP   rU   rY   )r+   r^   r)   r   r   r\   )rK   rM   retryermre
to_reraiser5   fr6   r3   r4   rW   rC   r7   s        r   DecoratedFunctionz+RetryOnException.<locals>.DecoratedFunction*  s    !%A-/G:%%ad66E/7 & 9 9   :??1%jA:a=99:s   ) 	A(,A##A()	functoolspartialr^   wraps)	rv   r3   r4   rC   r5   r6   r7   rW   rw   s	   ```````` r   r^   r^     sa    0 Y %A!'-/ / ??1: : : 
r   c                  B    t        t        j                         dz        S )Nr   )inttimer   r   r   rb   rb   =  s    	TYY[4	  r   c                 4    t        j                  | dz         y )Ng     @@)r}   sleep)r   s    r   rl   rl   A  s    **_v%&r   )r   
__future__r   r   r   collectionsrx   rh   r?   rA   rI   r}   googlecloudsdk.corer   abcre   AttributeErrorrp   objectr	   	Exceptionr   r&   r)   r+   r^   rb   rl   r   r   r   <module>r      s     ( &  '      
  * OO/
  +6 +$=Y =&)N )/> /kf k\ $D"1(,%)	5p!'}   / s   B BB