
    oiN                        d 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	 dZ
dZd Zdedee   fd	Zd
ede	e   fdZdedee	e   ef   fdZdedededeeef   fdZd
ededee	e   ef   fdZd
edee   fdZdedededeeee   f   fdZ	 	 d#dede	e   de	e   defdZedk(  rddlZddlZ eej<                        dkD  rej<                  d   ndZ eej<                        dkD  rej<                  d   ndZ  eej<                        dkD  rej<                  d   ndZ! eee e!       Z" e# ejH                  e"d!d"             yy)$ur  
schema_contract.py - Worker 스키마 계약 검증 verifier
workers/ 디렉토리를 자동 감지하여 SC-1 ~ SC-8 항목 검증

SC-1: Worker 디렉토리에 models.py 존재 확인
SC-2: Worker 디렉토리에 tests/test_contract.py 존재 확인
SC-3: shared/schemas/{name}.schema.json 존재 확인
SC-4: sample.normal.json → JSON Schema 검증
SC-5: sample.edge.json → JSON Schema 검증 (없으면 WARN)
SC-6: Pydantic 모델 필드 ↔ JSON Schema properties 일치 확인
SC-7: sample.json mtime vs .schema.ts mtime 비교 (sample이 더 새우면 WARN)
SC-8: Pydantic v1 문법(schema()) 사용 탐지 (WARN)
    N)Optionalz/home/jay/workspace/teamsz(/home/jay/workspace/teams/shared/schemasc                  .    	 ddl } | S # t        $ r Y yw xY w)uF   jsonschema 라이브러리를 임포트 시도. 없으면 None 반환.r   N)
jsonschemaImportError)r   s    B/home/jay/workspace/teams/dev4/qc/verifiers.bak/schema_contract.py_try_import_jsonschemar      s"     s    	workers_base_dirreturnc                 l   t         j                  j                  | dd      }g }t        j                  |      D ]T  }t         j                  j	                  |      t         j                  j                        }|j                  |d       V t         j                  j                  | ddd      }t        j                  |      D ]i  }t         j                  j	                  |      t         j                  j                        }t        fd|D              rV|j                  |d       k |S )u   
    workers_base_dir 하위에서 models.py 가 있는 디렉토리를 Worker로 감지.
    반환: [{"dir": "/abs/path/info_keyword", "name": "info_keyword"}, ...]
    *	models.py)dirnamec              3   .   K   | ]  }|d    k(    yw)r   N ).0w
worker_dirs     r   	<genexpr>z$_find_worker_dirs.<locals>.<genexpr>:   s     ;a1U8z);s   )ospathjoinglobdirnamebasenameappendany)r	   patternworkersmodels_pathworker_namepattern2r   s         @r   _find_worker_dirsr#   (   s    
 ggll+S+>GGyy) AWW__[1
gg&&z2z;?@A ww||,c3DHyy* EWW__[1
gg&&z2;7;;NN:{CDE N    r   c                 <   t         j                  j                  | d      }	 t        |dd      5 }|j	                         }ddd       t        j                  dt
        j                        }|r|j                  d      S 	 y# 1 sw Y   CxY w# t        $ r Y yw xY w)u   
    models.py 에서 __schema_name__ 변수를 파싱. 없으면 None.
    정규식으로 파싱하여 importlib 없이도 동작.
    r   rutf-8encodingNz*^__schema_name__\s*=\s*["\']([^"\']+)["\']   )
r   r   r   openreadresearch	MULTILINEgroupOSError)r   r    fcontentms        r   _read_schema_name_overrider5   @   s    
 '',,z;7K+sW5 	ffhG	IICWbll[771:  	 	
  s(   B B?B BB 	BBr   c                 &   	 t        | dd      5 }t        j                  |      dfcddd       S # 1 sw Y   yxY w# t        $ r
 dd|  fcY S t        j                  $ r}dd|  d| fcY d}~S d}~wt
        $ r}dd	|  d| fcY d}~S d}~ww xY w)
uC   JSON 파일 로드. (data, error_msg) 반환. 실패 시 data=None.r&   r'   r(    NzFile not found: zJSON parse error in : zOS error reading )r+   jsonloadFileNotFoundErrorJSONDecodeErrorr1   )r   r2   es      r   _load_json_filer>   Q   s    5$g. 	$!99Q<#	$ 	$ 	$ /'v... 8+D6A3777 5(b4445sG   = 1	= := = BB!	A0*B0B<	BBBdataschemasample_pathc                    	 |j                   } ||      }t        |j                  |       d       }|r_|dd D cg c]7  }dj                  d |j                  D              xs d d|j
                   9 }}d	d
| ddj                  |      z   fS dd| fS c c}w # t        $ r*}d	d| dt        |      j                   d| fcY d}~S d}~ww xY w)uG   jsonschema로 data를 schema에 대해 검증. (ok, detail_msg) 반환.c                 ,    t        | j                        S N)listr   )r=   s    r   <lambda>z/_validate_json_against_schema.<locals>.<lambda>e   s    4< r$   )keyN   .c              3   2   K   | ]  }t        |        y wrD   )str)r   ps     r   r   z0_validate_json_against_schema.<locals>.<genexpr>g   s     71A7s   z<root>r8   FzSchema validation FAILED for ; TzSchema validation PASSED: zjsonschema error validating )	Draft7Validatorsortediter_errorsr   r   message	Exceptiontype__name__)	r?   r@   jsonschema_modrA   validator_cls	validatorerrorsr=   msgss	            r   _validate_json_against_schemarZ   ^   s    	\&66!&)		--d39OP\bcede\fgWXsxx777C8DBqyykRgDg9+bIDIIVZO[[[1+??? h  \4[MDGDTDTCUUWXYWZ[[[\s4   :B  <B8B  B  B   	C)CCCr!   c                 H   t         j                  j                  | d      }d| d}	 t        j                  j                  ||      }|dd| fS t        j                  j                  |      }d}| t        j                  vr"t        j                  j                  d|        d}	 |j                  j                  |       |r2| t        j                  v r t        j                  j                  |        	 	 dd	lm} g }t        |      D ]Q  }	t!        ||	      }
	 t#        |
t$              r2t'        |
|      r&|
|ur"|
j(                  |k(  r|j+                  |	|
f       S |sdd| fS t/               }g }|D ]U  \  }}	 |j1                         }t/        |j9                  di       j;                               }||z  }|j+                  |       W |s
dd| d| fS |d| d| fS # |r3| t        j                  v r t        j                  j                  |        w w w xY w# t        $ r Y y
w xY w# t,        $ r Y .w xY w# t2        $ r& 	 |j5                         }n# t6        $ r i }Y nw xY wY w xY w# t6        $ r*}dd| dt%        |      j<                   d| fcY d}~S d}~ww xY w)u   
    models.py 를 동적으로 임포트하여 Pydantic 모델의 필드 목록을 반환.
    (fields_set_or_None, detail_msg) 반환.
    r   _sc_worker__modelsNzCannot create module spec for Fr   T)	BaseModel)Nz6pydantic is not installed; cannot inspect model fieldsz(No Pydantic BaseModel subclass found in 
propertieszNo fields found in models z in zPydantic model(s) z loaded from zDynamic import FAILED for r8   )r   r   r   	importlibutilspec_from_file_locationmodule_from_specsysinsertloaderexec_moduleremovepydanticr^   r   r   getattr
isinstancerS   
issubclass
__module__r   	TypeErrorsetmodel_json_schemaAttributeErrorr@   rR   getkeysrT   )r   r!   r    module_namespecmod
added_pathr^   model_classes	attr_nameattr
all_fields
used_namescls_nameclsschema_dictpropsr=   s                     r   _get_pydantic_fieldsr   n   s   
 '',,z;7K}G4K=Y~~55k;O<9+GGGnn--d3 
SXX%HHOOAz*J	,KK##C(jCHH4
+	R* S 	I3	*D	tT*"43I-;6!(()T):;	 CK=QQQ %

* 
	(MHc%!335 b9>>@AE%Jh'
	( 5j\k]SSS/
|=VVVY jCHH4
+ 5z  	RQ	R   " %%"%**,K  %"$K%%  Y1+baAQAQ@RRTUVTWXXXYs   (I. AI. 'G# 4I. 7H =I. AH,
I. 'I. <H<AI. 	I. #7HI. 	H)&I. (H))I. ,	H95I. 8H99I. <	I+II+I%"I+$I%%I+(I. *I++I. .	J!7JJ!J!c                    g }t        j                   t        j                  j                  | dd      d      }t	        j
                  d      }|D ]  }	 t        |ddd	      5 }t        |d
      D ]^  \  }}|j                  |      st        j                  j                  ||       }|j                  | d| d|j                                 ` 	 ddd        |S # 1 sw Y   xY w# t        $ r Y w xY w)u   
    Worker 디렉토리의 .py 파일에서 Pydantic v1 문법 (`.schema()`) 사용을 탐지.
    발견된 위치 목록 반환.
    z**z*.pyT)	recursivez\bschema\(\s*\)r&   r'   ignore)r)   rX   r*   :r8   N)r   r   r   r   r-   compiler+   	enumerater.   relpathr   rstripr1   )	r   hitspy_filesr   py_filer2   linenolinerels	            r   _detect_pydantic_v1_usager      s    
 Dyyj$?4PHjj+,G 	gsWXF H!$-aO HLFD~~d+ ggoogzBse1VHBt{{}o$FGHH KH H
  		s1   C*#$CA	CC*C'	#C**	C65C6workerschemas_dirjsonschema_missingc           
         | d   }| d   }g }d}d}t        |      }	|	r|	}
|j                  d| d|
 d       n|}
|j                  d| d|
 d       t        j                  j	                  ||
 d	      }t        j                  j	                  |d
      }t        j                  j                  |      r|j                  d|        n|j                  d|        d}t        j                  j	                  |dd      }t        j                  j                  |      r|j                  d|        n|j                  d|        d}t        j                  j                  |      r|j                  d|        n|j                  d|        d}d}t        j                  j                  |      r)t        |      \  }}|r|j                  d|        d}d}nd}t        j                  j	                  ||
 d      }|r|j                  d       n||j                  d       nt        j                  j                  |      s|j                  d|        d}nWt        |      \  }}|r|j                  d|        d}n0t        ||||      \  }}|j                  d|rdnd d|        |sd}t        j                  j	                  ||
 d      }|r|j                  d        n||j                  d!       nt        j                  j                  |      s|j                  d"|        d}nWt        |      \  }}|r|j                  d#|        d}n0t        ||||      \  }}|j                  d$|rdnd d|        |sd}||j                  d%       nt        |j                  d&i       j                               }|s|j                  d'|        d}nt        ||      \  }}||j                  d(|        d}n|j                  d)|        ||z
  }||z
  }|s|rfg }|r|j                  d*t        |              |r|j                  d+t        |              |j                  d,d-j	                  |              d}n|j                  d.t        |       d/       t        j                  j	                  ||
 d0      }||fD cg c]$  }t        j                  j                  |      s#|& }}t        j                  j                  |      s|j                  d1| d2       n|s|j                  d3       n	 t        j                  j                  |      }g } |D ]  }!t        j                  j                  |!      }"|"|kD  s(|"|z
  }#| j                  t        j                  j                  |!       d4|#d5d6t        j                  j                  |               | r&|j                  d7d-j	                  |       z          d}n|j                  d8       t#        |      }%|%rB|j                  d:| d;t        |%       d<       |%dd= D ]  }&|j                  d>|&         d}n|j                  d?| d       |rd}'|'|fS |rd@}'|'|fS d}'|'|fS c c}w # t         $ r }$|j                  d9|$        d}Y d}$~$d}$~$ww xY w)Au`   
    단일 Worker에 대해 SC-1 ~ SC-8 수행.
    (overall_status, details_list) 반환.
    r   r   FzWorker 'u   ': schema name override → ''z': schema name = 'z' (from dir name)z.schema.jsonr   zSC-1 PASS: models.py found: zSC-1 FAIL: models.py MISSING: Ttestsztest_contract.pyz)SC-2 PASS: tests/test_contract.py found: z+SC-2 FAIL: tests/test_contract.py MISSING: zSC-3 PASS: schema file found: z SC-3 FAIL: schema file MISSING: NzSC-3 FAIL: z.sample.normal.jsonzQSC-4 SKIP: jsonschema library not installed. Install with: pip install jsonschemaz;SC-4 SKIP: schema not loaded (SC-3 failed), cannot validatez'SC-4 FAIL: sample.normal.json MISSING: zSC-4 FAIL: zSC-4 PASSFAILr8   z.sample.edge.jsonzQSC-5 SKIP: jsonschema library not installed. Install with: pip install jsonschemaz;SC-5 SKIP: schema not loaded (SC-3 failed), cannot validatez'SC-5 WARN: sample.edge.json NOT FOUND: zSC-5 FAIL: zSC-5 z:SC-6 SKIP: schema not loaded (SC-3 failed), cannot comparer_   z6SC-6 WARN: JSON Schema has no 'properties' defined in u-   SC-6 FAIL: Cannot inspect Pydantic model — zSC-6 info: zin schema only: zin model only: u   SC-6 FAIL: Field mismatch — rM   z9SC-6 PASS: Pydantic fields match JSON Schema properties (z fields)z
.schema.tsz!SC-7 SKIP: .schema.ts not found (z), skipping mtime checkz5SC-7 SKIP: no sample.json files found for mtime checkz is z.0fzs newer than u4   SC-7 WARN: sample file(s) newer than .schema.ts — z<SC-7 PASS: .schema.ts is up-to-date relative to sample fileszSC-7 WARN: mtime check error: z4SC-8 WARN: Pydantic v1 .schema() usage detected in 'z' (z occurrence(s)):
   z  z7SC-8 PASS: No Pydantic v1 .schema() usage detected in 'WARN)r5   r   r   r   r   isfiler>   rZ   ro   rr   rs   r   rO   lengetmtimer   r1   r   )(r   r   rU   r   r   r!   detailshas_failhas_warnschema_name_overrideschema_baseschema_json_path	models_pytest_contract_pyschema_data
schema_errnormal_json_pathsample_data
sample_errokmsgedge_json_pathschema_propspydantic_fields
import_msgonly_in_schemaonly_in_modelmismatch_partsts_schema_pathrL   sample_paths_for_mtimets_mtimestale_samplessps_mtimediffr=   v1_hitshitstatuss(                                           r   _verify_workerr      s    J.KGHH 6jA*+.KK=XYZ[!+.@M^_`ww||KK=1MN Z5I	ww~~i 5i[AB7	{CD ww||J9KL	ww~~&'BCSBTUVDEUDVWX 
ww~~&'78H7IJK9:J9KLM	ww~~&'"12B"CZNN[56HK ww||KK=@S1TU3	
 
	TUWW^^,-@AQ@RST"12B"CZNN[56H3[.:JGB NNUR6V"<BseDE WW\\++>O/PQN3	
 
	TUWW^^N+@@PQR"1."AZNN[56H3[..GB NNUR6V"<BseDE ST;??<<AACDNNHIYHZ[ H*>z;*W'OZ&!Nzl[\ZL9:!-!? /, >!]%'N%&--.vn/E.FG %&---f].C-DE NN8>9R8ST  $HNN-.h8 WW\\++j/IJN$n59J  77>>.)//??VW	
 $NO	ww''7HM, ''**2.X%"X-D!((77++B/0T#JmBGGL\L\]kLlKmn	 Jii./  R (
3GB;- PG~-/	
 3B< 	'CNNRu:&	'PQ\P]]^_`  7? 
 7? 7?o<  	NN;A3?@H	s,   $Z-Z-A
Z2 BZ2 2	[;[[task_idc           
         |t         }|t        }g }|j                  d|         |j                  d|        |j                  d|        t        j                  j                  |      s|j                  d|        t               }|du }|r|j                  d       t        |      }|s|j                  d|        d|d	S |j                  d
t        |       d|D cg c]  }|d   	 c}        d}d}	|D ]n  }
|j                  d|
d    d|
d    d       t        |
|||      \  }}|j                  |       |j                  d|
d    d| d       |dk(  rd}g|dk(  smd}	p |rd}n|	rd}nd}||d	S c c}w )u  
    Worker 스키마 계약을 검증합니다.

    Args:
        task_id: 검증할 task ID (로깅용)
        workers_base_dir: Worker 디렉토리 탐색 기준 경로.
                          기본값: /home/jay/workspace/teams 하위 자동 탐색
        schemas_dir: JSON Schema 파일이 있는 디렉토리.
                     기본값: /home/jay/workspace/teams/shared/schemas/

    Returns:
        {"status": "PASS"|"FAIL"|"WARN"|"SKIP", "details": [...]}
    Nz Schema contract check for task: zWorkers base dir: zSchemas dir: zWARN: schemas_dir not found: zkWARN: jsonschema library not installed. SC-4 and SC-5 will be SKIPPED. Install with: pip install jsonschemaz,SKIP: No workers found (no models.py) under SKIP)r   r   zFound z worker(s): r   Fz
--- Worker: z (r   z) ---)r   r   rU   r   z--- Worker 'z
' result: z ---r   Tr   r   )DEFAULT_TEAMS_BASEDEFAULT_SCHEMAS_DIRr   r   r   isdirr   r#   r   r   extend)r   r	   r   r   rU   r   r   r   overall_has_failoverall_has_warnr   w_status	w_detailsfinal_statuss                 r   verifyr     s   $ -)GNN5gY?@NN'(8'9:;NN];-01 77==%6{mDE ,-N'4/R	
   01G:;K:LM	
 !W55NNVCL>'6RQqy6R5STU  $v'7r&-NO,#)1	
) 	y!fVn%5ZzNOv##$  	"w77; 7Ss   %F__main__r*   test      )r	   r   F)ensure_asciiindent)NN)%__doc__r   importlib.utilr`   r9   r   r-   rd   typingr   r   r   r   rK   rE   dictr#   r5   tupler>   boolrZ   ro   r   r   r   r   rT   _jsonr   argvtaskwdirsdirresultprintdumpsr   r$   r   <module>r      s      	 	 
  1 @  T
 03 8C= "
5# 
5%(;"< 
5\
\\;>\
49\ EYS EYs EYuXc]TWEW?X EYP# $s) 0OOO 	O
 3S	>On '+!%N8N8smN8 #N8 
	N8b zchh-!+388A;Dchh-!+388A;Dchh-!+388A;DD4TBF	+%++f5
;< r$   