o
    _�¿jg(  ã                   @  sØ   U d Z ddlmZ ddlZddlZddlZddlmZ ddlm	Z	m
Z
 ddlmZ ddlmZ e d¡ZG d	d
„ d
e	ƒZG dd„ deƒZeG dd„ dƒƒZG dd„ dƒZdaded< e ¡ Z		dddd„Zg d¢ZdS )u­  Thread-safe circuit breaker.

The circuit breaker prevents cascading failures by stopping fetch attempts
when a symbol repeatedly fails, allowing the exchange to recover.

States:

* **CLOSED** â€” normal operation; requests pass through.
* **OPEN** â€” failure threshold exceeded; requests are rejected immediately
  with :class:`~indiaopt.exceptions.CircuitOpenError`.
* **HALF-OPEN** â€” backoff period elapsed; one probe request is allowed
  through. Success â†’ CLOSED, failure â†’ OPEN (with reset backoff).

Each (exchange, symbol) pair has its own independent circuit state.

Example::

    breaker = CircuitBreaker(failure_threshold=5, backoff_seconds=300)

    # Inside your fetch loop:
    if breaker.is_open("NSE", "NIFTY"):
        raise CircuitOpenError(...)

    try:
        result = do_fetch()
        breaker.record_success("NSE", "NIFTY")
    except Exception:
        breaker.record_failure("NSE", "NIFTY")
        raise
é    )ÚannotationsN)Ú	dataclass)ÚEnumÚauto)Ú
NamedTuple)ÚCircuitOpenErrorzindiaopt.circuitc                   @  s"   e Zd ZdZeƒ Zeƒ Zeƒ ZdS )ÚCircuitStatezPossible states of a circuit.N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   ÚCLOSEDÚOPENÚ	HALF_OPEN© r   r   úK/home/dinkstrade/pdmp/venv/lib/python3.10/site-packages/indiaopt/circuit.pyr   .   s
    
r   c                   @  s   e Zd ZU ded< ded< dS )Ú_CircuitKeyÚstrÚexchangeÚsymbolN)r	   r
   r   Ú__annotations__r   r   r   r   r   6   s   
 r   c                   @  sJ   e Zd ZU ejZded< dZded< dZded< dZ	ded	< ddd„Z
dS )Ú_CircuitEntryr   Ústater   ÚintÚfailuresç        ÚfloatÚ
open_untilÚlast_failure_timeÚreturnÚNonec                 C  s   t j| _d| _d| _d S )Nr   r   )r   r   r   r   r   ©Úselfr   r   r   ÚresetB   s   
z_CircuitEntry.resetN©r   r    )r	   r
   r   r   r   r   r   r   r   r   r#   r   r   r   r   r   ;   s   
 r   c                   @  sz   e Zd ZdZ		d(d)d
d„Zd*dd„Zd+dd„Zd,dd„Zd-dd„Zd-dd„Z	d-dd„Z
d.d d!„Zd-d"d#„Zd/d%d&„Zd'S )0ÚCircuitBreakera¿  Thread-safe circuit breaker with CLOSED / OPEN / HALF-OPEN states.

    Args:
        failure_threshold: Consecutive failures before opening the circuit.
        backoff_seconds:   How long (seconds) the circuit stays open before
                           moving to HALF-OPEN for a probe attempt.

    Usage::

        breaker = CircuitBreaker(failure_threshold=5, backoff_seconds=300)

        key = ("NSE", "NIFTY")
        if breaker.is_open(*key):
            raise CircuitOpenError("NIFTY", open_until=..., failures=...)

        try:
            result = do_fetch()
            breaker.record_success(*key)
        except Exception:
            breaker.record_failure(*key)
            raise
    é   ç     Àr@Úfailure_thresholdr   Úbackoff_secondsr   r   r    c                 C  s@   |dk rt dƒ‚|dkrt dƒ‚|| _|| _i | _t ¡ | _d S )Né   zfailure_threshold must be >= 1r   zbackoff_seconds must be > 0)Ú
ValueErrorÚ_failure_thresholdÚ_backoff_secondsÚ	_circuitsÚ	threadingÚLockÚ_lock)r"   r(   r)   r   r   r   Ú__init__`   s   zCircuitBreaker.__init__Úkeyr   r   c                 C  s    || j vrtƒ | j |< | j | S )zBReturn (or create) the entry for *key*. Must be called under lock.)r.   r   )r"   r3   r   r   r   Ú
_get_entryp   s   

zCircuitBreaker._get_entryr   r   r   Úboolc                 C  sÀ   t | ¡ | ¡ ƒ}| j�J |  |¡}|jtjkr!	 W d  ƒ dS |jtjkrMt 	¡ }||j
krDtj|_t d||¡ 	 W d  ƒ dS 	 W d  ƒ dS 	 W d  ƒ dS 1 sYw   Y  dS )zÜReturn ``True`` if the circuit is open for *(exchange, symbol)*.

        When the backoff period has elapsed, transitions the circuit to
        HALF-OPEN and returns ``False`` so one probe attempt can proceed.
        NFõ7   Circuit HALF-OPEN for %s/%s â€” allowing probe attempt.T)r   Úupperr1   r4   r   r   r   r   ÚtimeÚ	monotonicr   r   ÚloggerÚinfo©r"   r   r   r3   ÚentryÚnowr   r   r   Úis_openx   s*   
ý
ýòñ$ïzCircuitBreaker.is_openr   c                 C  sH   t | ¡ | ¡ ƒ}| j� |  |¡jW  d  ƒ S 1 sw   Y  dS )zBReturn the current :class:`CircuitState` for *(exchange, symbol)*.N)r   r7   r1   r4   r   ©r"   r   r   r3   r   r   r   Ú	get_state’   s   
$ÿzCircuitBreaker.get_statec                 C  sx   t | ¡ | ¡ ƒ}| j�& |  |¡}|jdks|jtjkr&t 	d|||j¡ | 
¡  W d  ƒ dS 1 s5w   Y  dS )z}Record a successful fetch for *(exchange, symbol)*.

        Resets the circuit to CLOSED regardless of prior state.
        r   zBCircuit CLOSED for %s/%s after successful fetch (had %d failures).N)r   r7   r1   r4   r   r   r   r   r:   r;   r#   ©r"   r   r   r3   r=   r   r   r   Úrecord_success˜   s   
û
"özCircuitBreaker.record_successc                 C  sò   t | ¡ | ¡ ƒ}| j�c |  |¡}| jd7  _t ¡ |_|jt	j
kr:t	j|_t ¡ | j |_t d||| j¡ n-|j| jkr_t	j|_t ¡ | j |_t d|||j| j¡ W d  ƒ dS W d  ƒ dS W d  ƒ dS 1 srw   Y  dS )zwRecord a fetch failure for *(exchange, symbol)*.

        Opens the circuit when failures reach the threshold.
        r*   zGCircuit re-OPENED for %s/%s after probe failure. Backing off for %.0fs.zBCircuit OPENED for %s/%s after %d failures. Backing off for %.0fs.N)r   r7   r1   r4   r   r8   r9   r   r   r   r   r   r-   r   r:   Úwarningr,   rB   r   r   r   Úrecord_failureª   s:   

ûúîñ"øzCircuitBreaker.record_failurec                 C  sb   t | ¡ | ¡ ƒ}| j� || jv r| j|  ¡  W d  ƒ n1 s#w   Y  t d||¡ dS )z‡Manually reset the circuit for *(exchange, symbol)* to CLOSED.

        Useful in tests or operator-driven recovery scenarios.
        Nz!Circuit manually reset for %s/%s.)r   r7   r1   r.   r#   r:   r;   r@   r   r   r   r#   Ë   s   
€þzCircuitBreaker.resetc                 C  sL   | j � | j ¡ D ]}| ¡  q	W d  ƒ n1 sw   Y  t d¡ dS )z=Reset ALL circuits to CLOSED. Use with caution in production.NzAll circuits manually reset.)r1   r.   Úvaluesr#   r:   r;   )r"   r=   r   r   r   Ú	reset_allÖ   s   
ÿÿzCircuitBreaker.reset_allc                 C  s    t | ¡ | ¡ ƒ}| j�: |  |¡}|jtjkr>t ¡ }||j	k r+t
||j	|j|d�‚tj|_t d||¡ W d  ƒ dS W d  ƒ dS 1 sIw   Y  dS )zæRaise :class:`~indiaopt.exceptions.CircuitOpenError` if circuit is open.

        Convenience wrapper for the common check-and-raise pattern::

            breaker.raise_if_open("NSE", "NIFTY")  # raises or passes through
        )r   r   r   r   r6   N)r   r7   r1   r4   r   r   r   r8   r9   r   r   r   r   r:   r;   r<   r   r   r   Úraise_if_openÝ   s*   

üýó"þzCircuitBreaker.raise_if_openúdict[str, dict[str, object]]c                 C  s>   | j � dd„ | j ¡ D ƒW  d  ƒ S 1 sw   Y  dS )z�Return a snapshot of all circuit states for monitoring / metrics.

        Returns:
            Dict keyed by ``"EXCHANGE/SYMBOL"`` with state info.
        c                 S  s4   i | ]\}}|j › d |j› �|jj|j|jdœ“qS )ú/)r   r   r   )r   r   r   Únamer   r   )Ú.0r3   r=   r   r   r   Ú
<dictcomp>ÿ   s    ûýÿz(CircuitBreaker.stats.<locals>.<dictcomp>N)r1   r.   Úitemsr!   r   r   r   Ústatsø   s
   ú$ÿzCircuitBreaker.statsN©r&   r'   )r(   r   r)   r   r   r    )r3   r   r   r   )r   r   r   r   r   r5   )r   r   r   r   r   r   )r   r   r   r   r   r    r$   )r   rI   )r	   r
   r   r   r2   r4   r?   rA   rC   rE   r#   rG   rH   rO   r   r   r   r   r%   H   s    ý





!

r%   zCircuitBreaker | NoneÚ_default_breakerr&   r'   r(   r   r)   r   r   c                 C  sN   t � tdu rt| |d�aW d  ƒ tS W d  ƒ tS 1 s w   Y  tS )z½Return the module-level shared :class:`CircuitBreaker`.

    Creates it on first call with the given parameters. Subsequent calls
    return the same instance regardless of parameters.
    N©r(   r)   )Ú_default_breaker_lockrQ   r%   rR   r   r   r   Úget_default_breaker  s   
þ
þû
ÿúrT   )r%   r   rT   rP   )r(   r   r)   r   r   r%   )r   Ú
__future__r   Úloggingr/   r8   Údataclassesr   Úenumr   r   Útypingr   Úindiaopt.exceptionsr   Ú	getLoggerr:   r   r   r   r%   rQ   r   r0   rS   rT   Ú__all__r   r   r   r   Ú<module>   s,    
 Cþ