
    =i3(                    <   U d 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	m
Z
 ddZddZ ej                  d       ej                  d       ej                  d	       ej                  d
      gZded<   ddZddZddZdddZddZedk(  r e        yy)u  
impact_analyzer.py - 변경된 파일 목록을 입력받아 영향받는 함수/endpoint/모듈을 추출하는 정적 분석 도구.

사용법:
    python3 impact_analyzer.py --files "file1.py,file2.py" --output impact.json [--workspace /path/to/search]

제약:
    - LLM 호출 없음 (AST + grep + 정규식만 사용)
    - 실행 시간 30초 이내
    - Python 파일만 대상 (.py)
    - 존재하지 않는 파일은 에러 없이 무시
    - 빈 파일 목록은 빈 결과 반환
    )annotationsN)Pathc                   t         j                  j                  |       sg S 	 t        |       j	                  d      }t        j                  ||       }g }t        j                  |      D ]  }t        |t
        j                        s|j                  |j                         |j                  D ]W  }t        |t
        j                   t
        j"                  f      s.|j                  |j                   d|j                          Y  t        j$                  |      D ]H  }t        |t
        j                   t
        j"                  f      s.|j                  |j                         J |S # t        t        t        f$ r g cY S w xY w)u#  
    Python AST로 함수/클래스 정의를 파싱하여 이름 목록 반환.

    - ast.FunctionDef, ast.AsyncFunctionDef → 함수 이름
    - ast.ClassDef → 클래스 이름 + 클래스명.메서드명
    - 중첩 함수/메서드도 포함 (클래스명.메서드명 형태)
    utf-8encodingfilename.)ospathisfiler   	read_textastparseSyntaxErrorOSErrorUnicodeDecodeErrorwalk
isinstanceClassDefappendnamebodyFunctionDefAsyncFunctionDefiter_child_nodes)	file_pathsourcetreeresultsnodeitems         6/home/jay/workspace/teams/shared/qc/impact_analyzer.pyextract_functionsr%      s6    77>>)$	i**G*<yy)4 G ?dCLL)NN499%		 ?dS__c6J6J$KLNNdii[$))#=>?	? $$T* &dS__c.B.BCDNN499%& N% "45 	s   2E/ /FFc                j   t         j                  j                  |       sg S 	 t        |       j	                  d      }t        j                  ||       }g }t        j                  |      D ]  }t        |t
        j                        r-|j                  D ]  }|j                  |j                          Jt        |t
        j                         se|j"                  sr|j                  |j"                          t%        t&        j)                  |            S # t        t        t        f$ r g cY S w xY w)u   
    ast.Import, ast.ImportFrom 노드로 import 목록 추출.
    모듈 이름만 반환 (from X import Y → X 반환).
    r   r   r	   )r   r   r   r   r   r   r   r   r   r   r   r   Importnamesr   r   
ImportFrommodulelistdictfromkeys)r   r   r    r!   r"   aliass         r$   extract_importsr/   F   s    
 77>>)$	i**G*<yy)4 G ,dCJJ' +uzz*+ cnn-{{t{{+, g&'' "45 	s   2D D21D2z%self\.path\s*==\s*["\']([^"\']+)["\']z-self\.path\.startswith\(["\']([^"\']+)["\']\)z @\w+\.route\(["\']([^"\']+)["\']zC@\w+\.(get|post|put|patch|delete|head|options)\(["\']([^"\']+)["\']zlist[re.Pattern[str]]_ENDPOINT_PATTERNSc                   t         j                  j                  |       sg S 	 t        |       j	                  d      }g }t        D ]l  }|j                  |      D ]V  }|j                  r!|j                  dk\  r|j                  d      }n|j                  d      }||vsF|j                  |       X n |S # t
        t        f$ r g cY S w xY w)u   
    파일에서 HTTP endpoint 경로 문자열 추출.

    지원 패턴:
    - self.path == "..."
    - self.path.startswith("...")
    - @app.route("...")
    - @router.get/post/put/patch/delete("...")
    r   r         )r   r   r   r   r   r   r   r0   finditer	lastindexgroupr   )r   r   r!   patternmatchendpoints         r$   extract_endpointsr:   s   s     77>>)$	i**G*< G% )%%f- 	)E5??a#7 ;;q> ;;q>w&x(	)) N '( 	s   B7 7C
Cc                   | rt         j                  j                  |      sg S dt        j                  |        dt        j                  |        d}	 t        j                  dddd||gddd	
      }|j                  dvrg S |j                  j                         D cg c]#  }|j                         s|j                         % }}|S c c}w # t
        j                  t        f$ r g cY S w xY w)u   
    workspace 내 모든 .py 파일에서 module_name을 import하는 파일 목록 반환.

    검색 패턴:
    - from {module_name} import ...
    - import {module_name}
    z(from\s+z\s+import|import\s+z	(\s|$|,))grepz-rlz--include=*.pyz-ET
   )capture_outputtexttimeout)r   r3   )r   r   isdirreescape
subprocessrun
returncodestdout
splitlinesstripTimeoutExpiredFileNotFoundError)module_name	workspacer7   proclinefiless         r$   find_reverse_importsrQ      s     bggmmI6	 "))K011DRYY{E[D\\efG~~U,dGYG	
 ??&(I*.++*@*@*BS$djjlSS T%%'89 	s0   .C C C5CC C C.-C.c                   i }| D ]w  }t        |      j                  }t        ||      }|D cg c]D  }t        j                  j                  |      t        j                  j                  |      k7  sC|F }}|||<   y |S c c}w )u   
    각 변경 파일에 대해 역방향 import를 추적하여 딕셔너리 반환.

    반환 형식: {파일경로: [이 파일을 import하는 파일들]}
    )r   stemrQ   r   r   abspath)changed_filesrM   chainr   rL   	importersps          r$   build_dependency_chainrY      s     #%E" %	9o**(i@	 )^1RWW__Q-?277??S\C]-]Q^	^$i% L _s   AB2Bc                   | sg g g g i dS | D cg c]6  }|j                  d      st        j                  j                  |      s5|8 }}g }g }g }|D ]z  }t	        |      }|D ]  }||vs|j                  |        t        |      }	|	D ]  }
|
|vs|j                  |
        t        |      }|D ]  }||vs|j                  |        | t        ||      }| ||||dS c c}w )u$  
    변경된 파일 목록을 받아 영향 분석 결과 딕셔너리 반환.

    반환 형식:
    {
        "changed_files": [...],
        "affected_functions": [...],
        "affected_endpoints": [...],
        "affected_imports": [...],
        "dependency_chain": {...}
    }
    )rU   affected_functionsaffected_endpointsaffected_importsdependency_chainz.py)	endswithr   r   r   r%   r   r:   r/   rY   )rP   rM   fvalid_filesall_functionsall_endpointsall_importsr   funcs	endpointseimportsir^   s                 r$   analyzerj      s.    "$"$ " "
 	
 $Oqzz%'8RWW^^A=N1OKO!M!MK  &	!), 	(A%$$Q'	( &i0	 	(A%$$Q'	( "), 	&A#""1%	&&  .k9E ++', 1 Ps   C%C%C%c                    t        j                  d      } | j                  ddd       | j                  ddd	
       | j                  ddd
       | j                         }|j                  j                         }d|v r>|j                  d      D cg c]#  }|j                         s|j                         % }}n|r|gng }t        ||j                        }t        |j                        }|j                  j                  dd       t        |dd      5 }t        j                  ||dd       d d d        t!        d|        t!        dt#        |d                 t!        dt#        |d                 t!        dt#        |d                 t!        dt#        |d                 y c c}w # 1 sw Y   xY w) NuI   정적 분석으로 변경된 파일의 영향 범위를 파악합니다.)descriptionz--filesTuC   분석할 파일 목록 (쉼표 구분) 또는 단일 파일 경로)requiredhelpz--outputzimpact.jsonu?   결과를 저장할 JSON 파일 경로 (기본값: impact.json))defaultrn   z--workspacer   uO   역방향 import 탐색할 루트 디렉토리 (기본값: 현재 디렉토리),)rM   )parentsexist_okwr   r   Fr2   )ensure_asciiindentzImpact analysis written to: z  Changed files    : rU   z  Affected funcs   : r[   z  Affected endpoints: r\   z  Affected imports : r]   )argparseArgumentParseradd_argument
parse_argsrP   rI   splitrj   rM   r   outputparentmkdiropenjsondumpprintlen)parserargsrawr`   rP   resultoutput_paths          r$   mainr     s   $$1|}F
R  
 N  
 ^   D **


C
cz$'IIcN@qaggi@@"Udnn5Ft{{#KTD9	k3	1 ;Q		&!%:; 
(
67	!#f_&=">!?
@A	!#f-A&B"C!D
EF	"3v.B'C#D"E
FG	!#f-?&@"A!B
CD A; ;s   F6*F6F;;G__main__)r   strreturn	list[str])rL   r   rM   r   r   r   )rU   r   rM   r   r   zdict[str, list[str]])r   )rP   r   rM   r   r   r,   )r   None)__doc__
__future__r   rv   r   r   r   rB   rD   pathlibr   r%   r/   compiler0   __annotations__r:   rQ   rY   rj   r   __name__     r$   <module>r      s    #  
  	 	   P(F BJJ78BJJ?@BJJ23BJJUV	- ) 	LD06|&ER zF r   