
    !/il0                        d Z ddlZddlZddlZddlmZmZmZmZm	Z	m
Z
mZ ddlmZmZ ddlmZ ddlZe G d d             Ze G d d	             Z e       a ej(                         Z	 	 	 	 	 dAdedededededeeef   defdZdd
dddefdddededededededee	e   df   deeeeegdf      defdZ	 dBdd
dddefddddee   dedededededee	e   df   deeeeegdf      dee   defd Z G d! d"      Zdefd#ZdCd$Z e!d%k(  rddl"Z" e#e"jH                        d&kD  re"jH                  d&   Z%e%d'k(  r6 e&d(        e       Z'e'jQ                         D ]  \  Z)Z* e&d)e) d*e*         ye%d+k(  rH e&d,        e&d-       da+d.Z, ed/d01      d2        Z-	  e-       Z. e&d3e.         e&d5 e               ye%d6k(  rC e&d7        e0d/      D ]/  Z1 ee1d
d89      Z2 ee1d
d9      Z3 e&d:e1d&z    d*e2d;d<e3d;d=       1 y e&d>e%         e&d?       y e&d@        e&d?       yy# e$ rZ/ e&d4e/        Y dZ/[/dZ/[/ww xY w)Da  
Genesis Retry Utilities
=======================
Exponential backoff with jitter for resilient operations.

Usage:
    from retry_utils import retry, RetryConfig

    # As decorator
    @retry(max_attempts=3, base_delay=1.0)
    def flaky_operation():
        # May fail sometimes
        pass

    # With custom config
    config = RetryConfig(max_attempts=5, max_delay=60.0)

    @retry(config=config)
    def another_operation():
        pass

    # Programmatic retry
    result = retry_call(flaky_operation, max_attempts=3)
    N)CallableAnyOptionalTupleTypeUnionList)	dataclassfield)datetimec                       e Zd ZU dZdZeed<   dZeed<   dZ	eed<   dZ
eed	<   d
Zeed<   dZeeef   ed<   efZeee   df   ed<   dZeeeeegdf      ed<   y)RetryConfigz!Configuration for retry behavior.   max_attempts      ?
base_delay      N@	max_delay       @exponential_baseTjitter      ?g      ?jitter_range.
exceptionsNon_retry)__name__
__module____qualname____doc__r   int__annotations__r   floatr   r   r   boolr   r   	Exceptionr   r   r   r   r        )/mnt/e/genesis-system/core/retry_utils.pyr   r   #   s    +L#JIu!e!FD(2L%u%2/8lJd9os*+:BFHhxi 7 =>?Fr'   r   c                       e Zd ZU dZdZeed<   dZeed<   dZeed<   dZ	eed<   dZ
eed<   dZeed	<   d
Zee   ed<    ee      Zeed<   dedd
fdZdededd
fdZdefdZy
)
RetryStatsz Statistics for retry operations.r   total_callssuccessful_first_trysuccessful_after_retryfailed_all_attemptstotal_retries        total_delay_secondsN
last_error)default_factoryerrors_by_typeattemptsreturnc                     | xj                   dz  c_         |dk(  r| xj                  dz  c_        y | xj                  dz  c_        | xj                  |dz
  z  c_        y )N   )r+   r,   r-   r/   )selfr5   s     r(   record_successzRetryStats.record_success<   sQ    Aq=%%*%''1,'(Q,.r'   errorc                 *   | xj                   dz  c_         | xj                  dz  c_        | xj                  |dz
  z  c_        t        |      | _        t        |      j                  }| j                  j                  |d      dz   | j                  |<   y )Nr8   r   )	r+   r.   r/   strr2   typer   r4   get)r9   r5   r;   
error_types       r(   record_failurezRetryStats.record_failureD   s|    A  A% hl*e*%[))
*.*=*=*A*A*a*PST*TJ'r'   c                 n   | j                   | j                  | j                  | j                  | j                  | j                  | j                  z   t        d| j                         z  | j                  t        d| j                         z  t        | j                  d      | j                  | j                  d
S )Nr8      )
r+   r,   r-   r.   r/   success_ratefirst_try_rater1   r4   r2   )
r+   r,   r-   r.   r/   maxroundr1   r4   r2   r9   s    r(   to_dictzRetryStats.to_dictL   s    ++$($=$=&*&A&A#'#;#;!//!669T9TTq$"2"234"77#aAQAQ:RR#()A)A1#E"11//
 	
r'   )r   r   r   r    r+   r!   r"   r,   r-   r.   r/   r1   r#   r2   r   r=   r   dictr4   r:   r%   rA   rI   r&   r'   r(   r*   r*   0   s    *K !#!"#C#  M3!$$ $J$ 6ND6/s /t /Us U9 U U
 
r'   r*   r   r   r   Tattemptr   r   r   r   r   r6   c                 \    t        ||| z  z  |      }|rt        j                  | }||z  }|S )a  
    Calculate delay for the given attempt number.

    Uses exponential backoff with optional jitter:
    delay = min(base * (exp_base ^ attempt), max_delay) * jitter

    Args:
        attempt: Current attempt number (0-indexed)
        base_delay: Base delay in seconds
        max_delay: Maximum delay cap
        exponential_base: Multiplier for backoff
        jitter: Whether to add randomness
        jitter_range: Range for jitter multiplier

    Returns:
        Delay in seconds
    )minrandomuniform)rK   r   r   r   r   r   delayjitter_multipliers           r(   calculate_delayrR   a   s?    4 
.'9:IFE "NNL9""Lr'   r   r   r   r   r   r   r   r   funcr   r   .r   c          
      z   d}
d}t        |      D ]N  }	  | |i |	}t        5  t        j                  |dz          t        xj                  |z  c_        ddd       |c S  |
r|
y# 1 sw Y   xY w# |$ r}|}
|dz   |k\  rNt        5  t        j                  |dz   |       t        xj                  |z  c_        ddd        # 1 sw Y    xY wt        |||||      }||z  }|r ||dz   ||       t        d|dz    d| d|        t        d|d	d
       t        j                  |       Y d}~%d}~ww xY w)az  
    Call a function with retry logic.

    Args:
        func: Function to call
        *args: Positional arguments for func
        max_attempts: Maximum number of attempts
        base_delay: Initial delay between retries
        max_delay: Maximum delay cap
        exponential_base: Backoff multiplier
        jitter: Add randomness to delays
        exceptions: Exception types to catch and retry
        on_retry: Callback(attempt, exception, delay) on each retry
        **kwargs: Keyword arguments for func

    Returns:
        Result of successful function call

    Raises:
        Last exception if all attempts fail
    Nr0   r8   rK   r   r   r   r   z[Retry] Attempt /z	 failed: z[Retry] Waiting .2fzs before retry...)
range_stats_lock_global_statsr:   r1   rA   rR   printtimesleep)rT   r   r   r   r   r   r   r   argskwargslast_exceptiontotal_delayrK   resulterP   s                   r(   
retry_callre      s|   B +/NK& '&	4*6*F  A,,Wq[911[@1A M'T  KA A  	N {l*  E!001a@!55D5E E  $%#!1E 5 K 1a/ $Wq[M<.	!MN$U3K/@ABJJu9	sG   A22A&	A2&A/	+A22D:7D53C:	D5CA'D55D:)r   r   r   r   r   r   r   configrf   c                   |rT|j                   |j                  |j                  |j                  |j                  |j
                  |j                  dt        dt        ffd}	|  |	|       S |	S )a  
    Decorator for retry logic with exponential backoff.

    Can be used with or without parentheses:
        @retry
        def func(): ...

        @retry(max_attempts=5)
        def func(): ...

        @retry(config=RetryConfig(...))
        def func(): ...
    fr6   c           	      R     t        j                          fd       }|S )Nc                  2    t        g| 	d|S )NrS   )re   )
r_   r`   r   r   r   rh   r   r   r   r   s
     r(   wrapperz)retry.<locals>.decorator.<locals>.wrapper   s?    

)%#!1%!
 
 
r'   )	functoolswraps)	rh   rk   r   r   r   r   r   r   r   s	   ` r(   	decoratorzretry.<locals>.decorator   s*    			 	 
	 r'   )r   r   r   r   r   r   r   r   )
rT   r   r   r   r   r   r   r   rf   rn   s
    ```````  r(   retryro      s    4 **&&
$$	!22&&
??X (  " r'   c                       e Zd ZdZ	 	 	 	 	 ddededededef
dZd Zd	 Z	d
efdZ
ddZded
dfdZed
efd       Zed
efd       Zed
ee   fd       Zy)RetryContextaZ  
    Context manager for retry logic.

    Usage:
        with RetryContext(max_attempts=3) as ctx:
            while ctx.should_continue():
                try:
                    result = risky_operation()
                    ctx.success()
                    break
                except Exception as e:
                    ctx.failed(e)
    r   r   r   r   r   c                 t    || _         || _        || _        || _        || _        d| _        d| _        d | _        y )Nr   F)r   r   r   r   r   _attempt
_succeeded_last_error)r9   r   r   r   r   r   s         r(   __init__zRetryContext.__init__   s?     )$" 004r'   c                     | S Nr&   rH   s    r(   	__enter__zRetryContext.__enter__2  s    r'   c                      y)NFr&   )r9   exc_typeexc_valexc_tbs       r(   __exit__zRetryContext.__exit__5  s    r'   r6   c                 R    | j                   | j                  k  xr | j                   S )z"Check if should continue retrying.)rs   r   rt   rH   s    r(   should_continuezRetryContext.should_continue8  s#    }}t000H5HHr'   Nc                     d| _         y)zMark operation as successful.TNrt   rH   s    r(   successzRetryContext.success<  s	    r'   r;   c                    || _         | xj                  dz  c_        | j                  | j                  k  rt        | j                  dz
  | j                  | j
                  | j                  | j                        }t        d| j                   d| j                   d       t        d|dd       t        j                  |       y	y	)
z%Record failure and wait before retry.r8   rV   z[RetryContext] Attempt rW   z failedz[RetryContext] Waiting rX   zs...N)ru   rs   r   rR   r   r   r   r   r\   r]   r^   )r9   r;   rP   s      r(   failedzRetryContext.failed@  s     ==4,,,#)??..!%!6!6{{E +DMM?!D<M<M;NgVW+E#;d;<JJu -r'   c                     | j                   S )zGet number of attempts made.)rs   rH   s    r(   r5   zRetryContext.attemptsQ  s     }}r'   c                     | j                   S )zCheck if operation succeeded.r   rH   s    r(   	succeededzRetryContext.succeededV  s     r'   c                     | j                   S )zGet the last error.)ru   rH   s    r(   r2   zRetryContext.last_error[  s     r'   )r   r   r   r   Tr6   N)r   r   r   r    r!   r#   r$   rv   ry   r~   r   r   r%   r   propertyr5   r   r   r2   r&   r'   r(   rq   rq     s      "%55 5 	5
  5 5$I II $ " #   4    HY/    r'   rq   c                  b    t         5  t        j                         cddd       S # 1 sw Y   yxY w)zGet global retry statistics.N)rZ   r[   rI   r&   r'   r(   get_retry_statsr   a  s&    	 '$$&' ' 's   %.c                  N    t         5  t               addd       y# 1 sw Y   yxY w)zReset global retry statistics.N)rZ   r*   r[   r&   r'   r(   reset_retry_statsr   g  s#     
 %"% % %s   $__main__r8   statszGlobal Retry Stats:z  z: demoz$Demo: Retry with exponential backoffz(----------------------------------------rC      r   )r   r   c                  T    t         dz  a t         t        k  rt        dt                y)Nr8   zSimulated failure zSuccess!)
fail_count	max_failsConnectionErrorr&   r'   r(   flaky_operationr     s+     a
*),>zl*KLL!r'   z	
Result: z	
Failed: z
Stats: delayszDelay progression (5 attempts):F)r   r   z
  Attempt z.1fzs (with jitter: ~zs)zUnknown command: z0Usage: python retry_utils.py [stats|demo|delays]zGenesis Retry Utilities)r   r   r   Tr   rx   r   )4r    r]   rN   rl   typingr   r   r   r   r   r   r	   dataclassesr
   r   r   	threadingr   r*   r[   LockrZ   r!   r#   r$   rR   r%   re   ro   rq   rJ   r   r   r   syslenargvcmdr\   r   itemskvr   r   r   rc   rd   rY   irP   delay_jr&   r'   r(   <module>r      s  2    D D D (   	G 	G 	G (
 (
 (
X inn
 !(2!!! ! 	!
 ! u%! !N !/8lBFO
O O 	O
 O O O d9os*+O xi 7 =>?O 	Of  $7 !/8lBF$(7
8
7 7 	7
 7 7 7 d9os*+7 xi 7 =>?7 [!7 7tM  M `' '% z
388}qhhqk'>'(#%E %11#Rsm$% F]89(OJIc2" 3"((*
6(+, Io/012H_341X W'c%H)!DI
1Q3%r%4Egc]RTUVW %cU+,DE'(@A_ <  (
1#&''(s   H/ /I	4II	