
    { j~                       d Z ddlmZ ddlZddlZddlZddlmZmZ ddl	m	Z	m
Z
 ddlmZ ddlmZ ddlmZmZ dd	lmZ dd
lmZ dZdZdZdZd0dZ G d dee      Ze G d d             Zedef   Z G d d      Z e G d d             Z!d1dZ"d2dZ#	 	 	 	 	 	 	 	 d3dZ$dd	 	 	 	 	 	 	 	 	 d4dZ%d5dZ&dddddd ed!	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d6d"Z' G d# d$ee      Z(e G d% d&             Z)e G d' d(             Z*dd dded)	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d7d*Z+d dded+	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d8d,Z,d9d-Z-ddded.	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d:d/Z.y);u  utils/completion_callback_fallback_cancel.py

task-2553+9a — CALLBACK_FALLBACK_CANCEL_ON_SUCCESS (회장 결정).

목적: normal completion callback collector 가 result/report/collector-result
marker 생성을 durable 하게 완료했을 때, dispatch 시점에 사전 등록된 fallback
callback cron 을 자동 제거하여 뒤늦은 redundant 발화를 없앤다.

설계 원칙 (task-2553+9a §4, §9-R.1~§9-R.5):
  * callback orchestrator(utils/anu_delegation_completion_callback.py) **무수정**.
    본 모듈은 완전 분리·독립 — orchestrator 를 import/호출하지 않는다(§9-R.4).
  * cron remove 는 §9-R.1 5조건 결합 검증 전부 충족 시에만 (오발 제거 0).
  * success gate 는 §9-R.2 durable evidence 기반 — caller boolean 단독 금지.
  * cron-remove 실행은 dependency-injected `remover` (§9-R.5). 실 subprocess
    호출은 운영 collector 만, 본 task 구현/테스트는 fake/dry-run only.

분류(CancelClassification):
  CANCELLED              fallback cron 제거 성공 → fallback_cancelled=true
  ALREADY_GONE           이미 삭제됨(또는 만료) — idempotent, 실패 아님
  ALREADY_FIRED          이미 발화함 — 기존 DUPLICATE_CALLBACK_IGNORED 경로 유지
  SKIPPED_NORMAL_FAILED  durable evidence 부재/HOLD/failure/partial → fallback 보존
  SKIPPED_UNTRUSTED      §9-R.1 5조건 중 하나라도 불충족 → remove 미실행
  REMOVE_FAILED_WARNING  remove 시도 실패 → warning marker, collector success 유지
    )annotationsN)	dataclassfield)datetimetimezone)Enum)Path)CallableOptional)ANU_CALLBACK_KEY)fallback_schedule_registryl   L5: fallback)holdhold_for_chairfailfailedfailureerrorpartialcrashkilledtimeoutaborted	cancelledcanceledrunningpendingunknown)	okpasspassedsuccess	succeededcomplete	completeddonedefensive_hold_passc                 f    t        j                  t        j                        j	                  d      S )Nz%Y-%m-%dT%H:%M:%SZ)r   nowr   utcstrftime     ,utils/completion_callback_fallback_cancel.py_now_utcr/   K   s!    <<%../CDDr-   c                  $    e Zd ZdZdZdZdZdZdZy)CancelClassification	CANCELLEDALREADY_GONEALREADY_FIREDSKIPPED_NORMAL_FAILEDSKIPPED_UNTRUSTEDREMOVE_FAILED_WARNINGN)	__name__
__module____qualname__r2   r3   r4   r5   r6   r7   r,   r-   r.   r1   r1   O   s#    I!L#M3+3r-   r1   c                  8    e Zd ZU dZded<   dZded<   dZded<   y)	RemoverResultuN   cron remover 호출 결과. status: removed|already_gone|already_fired|failed.strstatus detailNOptional[dict]raw)r8   r9   r:   __doc____annotations__r@   rB   r,   r-   r.   r<   r<   X   s    XKFCCr-   r<   .c                  V    e Zd ZdZej
                  j                  dd      ZddddZy)	RealCokacdirCronRemoverun  실 `cokacdir --cron-remove` wrapper (운영 collector 전용 기본값).

    본 task 의 regression 은 fake remover 를 주입하므로 이 클래스의 subprocess
    경로는 테스트에서 절대 실행되지 않는다(§9-R.5). dry_run=True 면 subprocess
    호출 자체를 하지 않고 시뮬레이션 결과만 돌려준다(이중 안전장치).
    COKACDIR_BINz/usr/local/bin/cokacdirTdry_runc          	     \   |rt        ddd|d      S t        j                  | j                  d|dt	        t
              dt        gddd	
      }	 t        j                  |j                  j                         xs d      }|j                  d      dk(  rt        dd|      S t	        |j                  dd            j                         }d|v sd|v sd|v rt        d||      S t        d|xs d|      S # t        j                  $ r  d|j                  j                         d}Y w xY w)NremoveduE   dry-run: 실 subprocess 호출 0 (운영 collector 만 실제 제거)T)rI   cron_idr>   r@   rB   z--cron-removez--chatz--key<   )capture_outputtextr   z{}r   )r>   messager>   r   zcokacdir okrQ   r?   z	not foundzno suchalreadyalready_goner   remove failed)r<   
subprocessrunbinaryr=   ANU_CHAT_IDANU_KEYjsonloadsstdoutstripJSONDecodeErrorgetlower)selfrL   rI   procpayloadmsgs         r.   __call__z RealCokacdirCronRemover.__call__o   s.     ^ $9 
 ~~K   
	Jjj!2!2!4!<=G ;;x D( 	-WUU'++i,-335#c!1Y#5E sPPHS5KOQXYY ## 	J!(T[[5F5F5HIG	Js   1C8 80D+*D+N)rL   r=   rI   boolreturnr<   )	r8   r9   r:   rC   osenvironr_   rW   re   r,   r-   r.   rF   rF   e   s'     ZZ^^N,EFF8< Zr-   rF   c                      e Zd ZU ded<   ded<   ded<   ded<   ded<   ded	<    ee
      Zded<    ee
      Zded<   dZded<    ee	
      Z
ded<    ee	
      Zded<   dZded<   ddZy)CancelDecisionr1   classificationr=   task_idtarget_cron_idrf   cron_remove_invokedfallback_cancelledcancel_skipped_reason)default_factorydictsafe_remove_checksdurable_evidenceNrA   remover_resultlisthold_reasonsnotesr?   ts_utcc                &   d| j                   | j                  | j                  j                  | j                  | j
                  | j                  | j                  | j                  | j                  | j                  | j                  | j                  dS )N"callback_fallback_cancel_result_v1)schemarm   rn   rl   ro   rp   rq   rt   ru   rv   rx   ry   rz   )rm   rn   rl   valuero   rp   rq   rt   ru   rv   rx   ry   rz   ra   s    r.   to_dictzCancelDecision.to_dict   s{    :||"11"1177#'#;#;"&"9"9%)%?%?"&"9"9 $ 5 5"11 --ZZkk
 	
r-   rg   rs   )r8   r9   r:   rD   r   rs   rt   ru   rv   rw   rx   ry   rz   r   r,   r-   r.   rk   rk      s|    ((L$T::"48d8%)NN)t4L$4-E4-FC
r-   rk   c                    	 t        j                  | j                  d            S # t        t         j                  f$ r Y y w xY w)Nutf-8encoding)rZ   r[   	read_textOSErrorr^   )paths    r.   
_read_jsonr      s>    zz$..'.:;;T))* s   $' AAc                    | xs dj                         j                         syt        D ]  }|v s y t        fdt        D              S )Nr?   Fc              3  4   K   | ]  }|k(  xs |v   y wNr,   ).0tokss     r.   	<genexpr>z%_status_is_success.<locals>.<genexpr>   s!     Fsax#3!8#Fs   )r]   r`   _FAILURE_STATUS_TOKENSany_SUCCESS_STATUS_TOKENS)r>   r   r   s     @r.   _status_is_successr      sQ    	2$$&A% !8 F/EFFFr-   c                "   dddddddd}t        |       }|du|d<   |j|j                  d      xs; |j                  d      xs( |j                  d      xs |j                  d	      xs d}||d
<   t        t        |            |d<   |j	                         xr |j                         j                  dkD  |d<   |j	                         |d<   |d   sd|d<   |S |d   sd|d
   d|d<   |S |d   sd|d<   |S |d   sd|d<   |S d|d<   d|d<   |S )u   §9-R.2: result.json(존재 AND status 성공/비-HOLD/비-failure) + report +
    collector-result marker 가 실재·정합할 때만 success. boolean 은 권위 아님.FNr?   )result_json_existsresult_json_statusresult_json_status_okreport_existscollector_result_marker_exists	satisfiedreasonr   r>   rl   final_statusresultr   r   r   r   r   u8   result.json 부재 → normal collector 미완료/실패r   u,   result.json status 비-성공/HOLD/failure ()u"   report 부재 또는 비어 있음u   collector-result marker 부재Tr   u5   durable evidence 정합 (result+status+report+marker))r   r_   r   r=   existsstatst_size)result_json_pathreport_pathcollector_result_marker_pathevrjr>   s         r.   evaluate_durable_evidencer      s    $"!&*/B 
$	%B!~B	~FF8 vv&'vvn% vvh  	 $* &8V&E"#?!1!1!3!;!;a!?  ,H+N+N+PB'("#Q8 I '(:2>R;S:VVWX 	8 I  ;8 I 0178 I ;N8Ir-   )callback_contractc                "   ddddddddddd
}t        |      }|d|d<   |S d|d	<   |j                  d
      }t        |t              sd|d<   |S |j                  d      }||d<   t	        |xr t        |t
              xr ||k(        |d<   |j                  d      | k(  |d<   |j                  d      t        k(  xr |j                  d      t        k(  |d<   |j                  d      t        k(  |d<   t	        |      xr |d   |d<   |.|j                  d      }|d|d<   n||k(  rd|d<   n
d|d<   d|d<   t        |d   |d   |d   |d   |d   f      |d<   |d   s+dD 	cg c]	  }	||	   s|	 }
}	ddj                  |
      z   |d<   |S c c}	w )u   §9-R.1 5조건. dispatch-fired marker 의
    callback_policy_a.fallback_callback_cron_id 가 단일 권위.
    callback contract 는 동일값 교차확인용 보조일 뿐 단독 권위 아님.FNzn/ar?   )
c1_marker_id_matchesc2_task_bindingc3_ownershipc4_role_fallbackc5_not_stale_or_typoall_satisfiedmarker_presentauthority_cron_idcontract_cross_checkfail_reasonuQ   dispatch-fired marker 부재/파싱불가 → 추정 remove 0 (SKIPPED_UNTRUSTED)r   Tr   callback_policy_auB   marker.callback_policy_a 부재 → fallback_cron_id 권위 없음fallback_callback_cron_idr   r   rm   r   chat_idanu_keyr   fallback_roler   r   contract_no_fallback_idr   matchMISMATCHr   )r   r   r   r   r   u   5조건 미충족: ,)r   r_   
isinstancers   rf   r=   rX   rY   FALLBACK_ROLEalljoin)rm   rn   dispatch_fired_marker_pathr   checksmarkerpolicyauthority_idcontract_idkr   s              r.   evaluate_safe_remover      sH    !& ! %! %F 23F~_ 	} #FZZ+,Ffd#P 	} ::9:L".F &* 	+|S)	+N*&F!" !'

9 5 @F 	

9, 	-JJy!W, > 	

?#}4  &*,%7 &F=F!"
 $'++,GH-FF)*L(-4F)*-7F)*-2F)*!)*$%>"%&)*	
F? /"

 !9 

 

 !68H H}M

s   #Fc                   	 | j                   j                  dd       t        j                  t	        |       t        j
                  t        j                  z  t        j                  z  d      }t        j                  |dd      5 }|j                  t        j                  t               t        j                         d             d	d	d	       y# t        $ r Y yw xY w# 1 sw Y   yxY w)
u   O_CREAT|O_EXCL atomic — normal-success-cancel vs fallback-fire race 에서
    단일 처리만 허용(이중 처리·재escalate 0).Tparentsexist_oki  Fwr   r   )	locked_atpidN)parentmkdirrh   openr=   O_CREATO_EXCLO_WRONLYFileExistsErrorfdopenwriterZ   dumpsr/   getpid)	lock_pathfdfhs      r.   _acquire_cancel_lockr   `  s    td;WWS^RZZ"))%;bkk%I5Q 
2sW	- L
(*RYY[IJKL	  Ls   A-C AC!	CC!C*FT)fallback_cancelled_marker_pathcancel_lock_pathr   normal_collector_successremoverrI   now_fnc                t   |

t               }
 |       }t        |||      }t        |	      |d<   |d   s)t        t        j
                  | |ddd|d    |dg|	      S t        | |||	      }|d
   s]g }|d   s|j                  d       |d   dk(  r|j                  d       t        t        j                  | |ddd|d    d|||dg|      S |-t        |      s"t        t        j                  | |ddd|||	      S  |
||      }|j                  |j                  |j                  d}|j                  dk(  rv|O|j                  j                  dd       |j!                  t#        j$                  d| |d||||ddd      d !       t        t        j&                  | |ddd"|||d#g|$      S |j                  d%k(  r#t        t        j(                  | |ddd&||||'
      S |j                  d(k(  r#t        t        j                  | |ddd)||||'
      S t        t        j*                  | |ddd*|||d+g|$      S ),u   normal collector 성공 시 사전등록 fallback callback cron 자동 제거.

    `normal_collector_success` 는 보조 신호일 뿐 §9-R.2 durable-evidence gate 에
    종속한다 — 단독으로 cancel 을 결정하지 않는다.
    )r   r   r   caller_boolean_auxr   FuW   normal collector durable evidence 미충족 → fallback 보존 (예정대로 발화): r   us   §9-R.2: caller boolean 은 권위 아님 — durable evidence 부재 시 boolean=true 라도 SKIPPED_NORMAL_FAILED)	rl   rm   rn   ro   rp   rq   ru   ry   rz   )rm   rn   r   r   r   r   u?   dispatch-fired marker 부재 — fallback_cron_id 신뢰 불가r   r   uB   callback contract vs marker 권위 id MISMATCH — 타 cron 위험u@   §9-R.1 5조건 결합 검증 실패 → cron remove 미실행 (r   r   uQ   오발 제거 0: 신뢰할 수 없는 fallback_cron_id 는 절대 추정 remove 0)rl   rm   rn   ro   rp   rq   rt   ru   rx   ry   rz   u   동시 cancel 락 획득 실패 — 다른 처리기가 단일 처리 중 (이중 처리·재escalate 0, DUPLICATE 경로 유지))	rl   rm   rn   ro   rp   rq   rt   ru   rz   rH   rM   rK   Tr   fallback_cancelled_v1)r}   rm   r   rp   rI   rz   rt   ru      )ensure_asciiindentr   r   r?   z&fallback_cancelled=true marker persist)rl   rm   rn   ro   rp   rq   rt   ru   rv   ry   rz   rS   u@   fallback cron 이미 삭제/만료 — idempotent, 실패 아님)
rl   rm   rn   ro   rp   rq   rt   ru   rv   rz   already_fireduJ   fallback 이미 발화 — 기존 DUPLICATE_CALLBACK_IGNORED 경로 유지u   cron remove 실패 — warning marker. normal collector success 는 실패로 바꾸지 않음 (§3.5). fallback 은 DUPLICATE 경로로 음소거됨z2collector success preserved despite remove failure)rF   r   rf   rk   r1   r5   r   appendr6   r   r4   r>   r@   rB   r   r   
write_textrZ   r   r2   r3   r7   )rm   rn   r   r   r   r   r   r   r   r   r   rI   r   tsr   r   rx   rrremover_dicts                      r.   cancel_fallback_on_successr   p  s   * )+	B 
#)%A
B
  $$<=Bk?/EE) %$**,X,9  < 
 	
& "%#=+	F /"&'Q ()Z7T /AA) %$=)*!-  &%c !
 	
( #,@AQ,R/==) %$J  &
 	
  
	1B ii299RVVLL	yyI)5*1177t7T*55

"9#*5C.2#*"$.4,.	 "' ! 6 " /99) $#"$%';<
 	
 
yyN"/<<) $$"d%'
 	
 
yyO#/==) $$\%'
 	
  +AA%  ` "#CD r-   c                  $    e Zd ZdZdZdZdZdZdZy)
PruneCausecancel_not_wiredcancel_failedschedule_not_foundstale_round
stale_head!normal_callback_already_collectedN)	r8   r9   r:   CANCEL_NOT_WIREDCANCEL_FAILEDSCHEDULE_NOT_FOUNDSTALE_ROUND
STALE_HEAD!NORMAL_CALLBACK_ALREADY_COLLECTEDr,   r-   r.   r   r   2  s#    )#M-KJ(K%r-   r   c                  z    e Zd ZU 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dZy)PruneOutcomer=   rm   introundhead_sharL   r1   rl   Optional[str]causerf   ro   prunedr@   rz   c                    | j                   | j                  | j                  | j                  | j                  j
                  | j                  | j                  | j                  | j                  | j                  d
S )N
rm   r  r  rL   rl   r  ro   r  r@   rz   )rm   r  r  rL   rl   r~   r  ro   r  r@   rz   r   s    r.   r   zPruneOutcome.to_dictK  s\    ||ZZ||"1177ZZ#'#;#;kkkkkk
 	
r-   Nr   r8   r9   r:   rD   r   r,   r-   r.   r  r  >  s<    LJML((LKK
r-   r  c                  \    e Zd ZU ded<   ded<   ded<   ded<   ded	<   ded
<   ded<   ddZy)SelfCheckDecisionrf   proceedr  r  r=   r@   rm   r  r  r  rz   c                    | j                   | j                  | j                  | j                  | j                  | j
                  | j                  dS )Nr  r  r@   rm   r  r  rz   r  r   s    r.   r   zSelfCheckDecision.to_dictg  s=    ||ZZkk||ZZkk
 	
r-   Nr   r
  r,   r-   r.   r  r  ]  s+    MKLJMK	
r-   r  )r   rI   canonical_rootregistry_pathr   c        	           |
t               }t        j                  | ||||      }	|	sg S g }
|	D ]~  }	  ||j                  |      } |       }|j                  dk(  rpt
        j                  }d}d}d}|j                  xs d}t        j                  |j                  |j                  |j                  |j                  d| |||       n|j                  dk(  rt
        j                  }t        j                  j                  }d}d}|j                  xs d}t        j                  |j                  |j                  |j                  |j                  t        j                  j                  |||       n|j                  d	k(  rt
        j                   }t        j"                  j                  }d}d}|j                  xs d	}t        j                  |j                  |j                  |j                  |j                  t        j"                  j                  |||       n>t
        j$                  }t        j&                  j                  }d}d
}|j                  xs d}|
j)                  t+        |j                  |j                  |j                  |j                  ||||||
              |
S # t,        $ r}|
j)                  t+        |j                  |j                  |j                  |j                  t
        j$                  t        j&                  j                  dd
dt/        |      j0                   d|  |       
             Y d}~#d}~ww xY w)u   registry 기반 idempotent prune.

    recs가 비어있으면 [] 반환 (이미 prune됨/없음 — idempotent 재실행 안전).
    각 rec에 대해 remover를 호출하고 PruneOutcome 리스트를 반환.
    Nr  r  rH   rK   T
pruned_on_)rm   r  r  rL   r  r  r  r   rS   r   FrT   r	  zREMOVE_FAILED_WARNING: z: )rF   r   pending_forrL   r>   r1   r2   r@   mark_prunedrm   r  r  r3   r   r   r~   r4   r   r7   r   r   r  	Exceptiontyper8   )rm   r  r  triggerr   rI   r  r  r   recsoutcomesrecr   r   rl   r  ro   r  r@   excs                       r.   prune_fallbacks_for_keyr  v  s   " )+%11%#D 	H ZY	g6BByyI%!5!?!?&*#/i*66KK)) \\KK&wi0#1"/!	 n,!5!B!B"55;;&*#4n*66KK)) \\KK$77==#1"/!	 o-!5!C!C"DDJJ&*#5o*66KK)) \\KK$FFLL#1"/!	 "6!K!K"0066&*#5oOOKK)) \\KK#1(;!!{Zv O!  	OOKK)) \\KK#7#M#M$2288(, 4T#Y5G5G4H3%P!8 	s   I:J33	M<BMM)dispatch_firedr  r  r   c                $   |syt        j                  ||      }|D cg c]$  }|j                  | k(  s|j                  |k(  s#|& }	}|	s@t	        | ||dt
        j                  t        j                  j                  ddd |       
      S yc c}w )u   cancel_not_wired 근본원인 코드화.

    dispatch_fired가 True인데 registry에 (task_id, round) 매칭 레코드가
    하나도 없으면 → fallback이 등록됐으나 registry 미기록 상태.
    Nr  r?   Fui   fallback dispatch fired but absent from durable registry — collector cannot prune (A86DB611 root cause)r	  )
r   read_recordsrm   r  r  r1   r6   r   r   r~   )
rm   r  r  r  r  r  r   r  rmatcheds
             r.   detect_unwired_fallbackr$    s     %22%#D
 LQ!))w"6177e;KqLGL/AA--33 %C 8
 	
 # Ms   BBBc                    t        d | D              }t        d | D              }| D cg c]  }|j                   }}|||dS c c}w )u  PruneOutcome list를 live_prune 요약 dict로 변환.

    real_tombstone: pruned=True AND cron_remove_invoked=True 인 건이 1건 이상 존재.
    pruned_count: pruned=True 건수.
    causes: 각 outcome의 cause 값 list (PruneCause enum 보존).
    c              3  P   K   | ]  }|j                   xr |j                     y wr   )r  ro   r   os     r.   r   z'summarize_live_prune.<locals>.<genexpr>,  s'      /0*Q***s   $&c              3  :   K   | ]  }|j                   sd   yw)   N)r  r'  s     r.   r   z'summarize_live_prune.<locals>.<genexpr>/  s     7Qahhq7s   )real_tombstonepruned_countcauses)r   sumr  )r  r+  r,  r(  r-  s        r.   summarize_live_pruner/  %  s\      4< N 7(77L'(!agg(F(($  )s   A)current_roundcurrent_head_shanormal_callback_collectedr   c           	     p    |       }|r*t        dt        j                  j                  d| |||      S |6||k  r1t        dt        j                  j                  d| d| d| |||      S |r8|r6||k7  r1t        dt        j
                  j                  d|d	|d
| |||      S t        ddd| |||      S )u`  fallback cron 실행 시점 2계층 방어 stale self-check.

    우선순위:
    1. normal_callback_collected True → NORMAL_CALLBACK_ALREADY_COLLECTED
    2. current_round is not None and round < current_round → STALE_ROUND
    3. current_head_sha and head_sha and head_sha != current_head_sha → STALE_HEAD
    4. 그 외 → proceed=True
    Fur   normal callback already collected — fallback must not fire (stale self-check: NORMAL_CALLBACK_ALREADY_COLLECTED)r  Nzfallback round z < current round u/    — stale fallback must not fire (STALE_ROUND)zfallback head_sha z != current head_sha u.    — stale fallback must not fire (STALE_HEAD)Tz@recovery-only: no stale condition, fallback may fire as recovery)r  r   r   r~   r   r   )rm   r  r  r0  r1  r2  r   r   s           r.   fallback_self_checkr4  ;  s   $ 
B  >>DDH 
 	
  U]%: ((..!%(9- IA A 
 	
 H5E)E ''--$XL0E#&&TV 
 	
 Q r-   )rg   r=   )r   r	   rg   rA   )r>   r=   rg   rf   )r   r	   r   r	   r   r	   rg   rs   )
rm   r=   rn   r=   r   r	   r   rA   rg   rs   )r   r	   rg   rf   )rm   r=   rn   r=   r   r	   r   r	   r   r	   r   r	   r   Optional[Path]r   r5  r   rA   r   rf   r   Optional[Remover]rI   rf   r   Callable[[], str]rg   rk   )rm   r=   r  r  r  r=   r  r=   r   r6  rI   rf   r  r  r  r  r   r7  rg   rw   )rm   r=   r  r  r  r=   r  rf   r  r  r  r  r   r7  rg   zOptional[PruneOutcome])r  rw   rg   rs   )rm   r=   r  r  r  r=   r0  zOptional[int]r1  r  r2  rf   r   r7  rg   r  )/rC   
__future__r   rZ   rh   rU   dataclassesr   r   r   r   enumr   pathlibr	   typingr
   r   utils.callback_envelope_schemar   rY   utilsr   rX   r   r   r   r/   r=   r1   r<   RemoverrF   rk   r   r   r   r   r   r   r   r  r  r  r$  r/  r4  r,   r-   r.   <module>r@     sx  0 #  	  ( '   % F ,  $
 E43 4    3%
&(Z (ZV 
 
 
FG// / #'	/
 
/t )-__ _ !%	_
 &_ 
_J
0 6:'+(,%*!% (|| | !%	|
 | | #'| %3| %| &| #| | | | |DLd L 
 
 
< 
 
 
< "&$(#' ({{ { 	{
 { { { "{ !{ { 
{L  $(#' ((( ( 	(
 ( "( !( ( (\6 $(&*&+ (FF F 	F
 !F $F  $F F Fr-   