
    j+                        d Z ddlmZ ddlZddlmc mZ ddl	Z	ddl
ZddlZddlZddlmZ ddlZ ee      j'                         Zej*                  d   Zedz  dz  Z ej0                  d	      d
        Zd Zd Zd Zy)u]  tests/regression/test_dispatch_smoke_2569_plus_1.py

회귀 테스트 — task-2569+1 dispatch.py UnboundLocalError hotfix.

배경 (회장 §명시, 2026-05-13):
  task-2569 RC-4 영역에서 dispatch/__init__.py 에 4 위치
  `git update-index --add --intent-to-add` 코드를 추가하면서
  함수 내부에 `import subprocess` local import 가 들어갔다.

  Python LEGB rule:
    - 함수 안에 `import X` 가 있으면 함수 전체에서 X 는 local variable.
    - top-level `import subprocess` (line 30) 가 함수 내부에서 무시됨.
    - 함수 안 line 3508 `subprocess.run(timer_cmd, ...)` 이 local
      import (line 3590, 4101) 보다 위에 있어 UnboundLocalError 발생.

  결과: dispatch.py 모든 호출 broken. 위임 시스템 자체 마비.

본 회귀 (3 카운트):
  1. dispatch 모듈 import smoke — `from dispatch import dispatch` 가
     예외 없이 통과해야 한다. (top-level import subprocess 정상 결합)
  2. 함수 내부 `import subprocess` 패턴 0건 박제 — local import 가
     재발하면 본 테스트가 실패하면서 UnboundLocalError 의 근본 원인을
     선제적으로 차단한다.
  3. dispatch 함수 내부에서 subprocess 가 LEGB 의 module-level 로
     해석되는지 확인 — `dispatch.subprocess` 가 표준 stdlib subprocess
     모듈과 동일한 객체여야 한다.
    )annotationsN)Path   dispatchz__init__.pymodule)scopec                    t         j                  j                  dd       t        j                  j                  dt        t        t        j                        g      } g }d}| |u}|}|r| j                  }d}||u}|}|st        j                  d|fd| |f      dt        j                         v st        j                  |       rt        j                  |       ndt        j                  |      dz  }dd	|iz  }	|j!                  |	       |rt        j                  dfd
f      dt        j                         v st        j                  |       rt        j                  |       ndt        j                  |      t        j                  |      dz  }
dd|
iz  }|j!                  |       t        j"                  |d      i z  }t        j$                  dt               dz   d|iz  }t'        t        j(                  |            dx}x}x}x}x}x}}t        j                  j+                  |       }|t         j                  d<   | j                  j-                  |       |S )u8  `/home/jay/workspace/dispatch/__init__.py` 를 파일 경로로 직접 로드.

    pytest 의 rootdir 가 `tests/` 일 때 `tests/dispatch/__init__.py`
    빈 패키지가 진짜 dispatch 패키지를 가리는 사고를 막기 위해
    importlib.util.spec_from_file_location 으로 강제 결합한다.
    r   N)submodule_search_locations)is not)z%(py2)s is not %(py5)sspec)py2py5z%(py7)spy7)z5%(py11)s
{%(py11)s = %(py9)s.loader
} is not %(py14)s)py9py11py14z%(py16)spy16r   u   dispatch spec 로드 실패: z
>assert %(py19)spy19)sysmodulespop	importlibutilspec_from_file_locationDISPATCH_INITstrparentloader
@pytest_ar_call_reprcompare@py_builtinslocals_should_repr_global_name	_safereprappend_format_boolop_format_assertmsgAssertionError_format_explanationmodule_from_specexec_module)r   @py_assert1@py_assert4@py_assert3@py_assert0@py_assert10@py_assert13@py_assert12@py_format6@py_format8@py_format15@py_format17@py_format18@py_format20r   s                  G/home/jay/workspace/tests/regression/test_dispatch_smoke_2569_plus_1.pydispatch_moduler:   0   s    KKOOJ%>>11Ms=CWCW?X>Y 2 Dt 4t  4 4 7   4t                   4      !%    !%    !,    48        (7      ^^,,T2F$CKK
KKF#M    c                   d}t        | |      }|st        j                  d      dz   dt        j                         v st        j
                  t               rt        j                  t               nddt        j                         v st        j
                  |       rt        j                  |       ndt        j                  |      t        j                  |      dz  }t        t        j                  |            dx}}| j                  }t        |      }|st        j                  d      d	z   d
t        j                         v st        j
                  t              rt        j                  t              nd
dt        j                         v st        j
                  |       rt        j                  |       ndt        j                  |      t        j                  |      dz  }t        t        j                  |            dx}}y)u-  `from dispatch import dispatch` 가 예외 없이 통과해야 한다.

    UnboundLocalError 가 재발하면 함수 정의 자체는 통과하지만 실제
    호출 직전까지 잠복한다. 본 smoke 는 적어도 모듈 로드 + 함수
    심볼 노출까지가 정상임을 박제한다.
    r   uF   dispatch 모듈에서 dispatch 함수 심볼이 노출되지 않았다z7
>assert %(py5)s
{%(py5)s = %(py0)s(%(py1)s, %(py3)s)
}hasattrr:   )py0py1py3r   Nu:   dispatch.dispatch 가 호출 가능한 객체가 아니다zL
>assert %(py5)s
{%(py5)s = %(py0)s(%(py3)s
{%(py3)s = %(py1)s.dispatch
})
}callable)r=   r   r'   r!   r"   r#   r$   r(   r)   r   rA   )r:   @py_assert2r-   r3   s       r9   'test_regression_1_dispatch_import_smokerC   E   s    %/ 7?J/ /   	Q               #    #    %/    0      $,, 8,- -   	E               $    $    -    .     r;   c                 `   t         j                  d      } t        j                  | t	        t                     }g  G fddt        j
                        } |       j                  |       g }|k(  }|st        j                  d|fd|f      dt        j                         v st        j                        rt        j                        ndt        j                  |      d	z  }t        j                  d
 d      dz   d|iz  }t        t        j                  |            dx}}y)u  dispatch/__init__.py 함수 내부 local `import subprocess` 0건 박제.

    Python LEGB 규칙 상 함수 내부 `import X` 는 X 를 함수 local 로
    바인딩한다. top-level `import subprocess` (line 30) 가 있는 상태
    에서 함수 내부에 동일한 import 가 추가되면, 함수 안에서 해당 import
    행 보다 앞에 있는 `subprocess.run(...)` 호출이 모두 UnboundLocalError
    로 깨진다 — task-2569 RC-4 가 정확히 이 패턴이었다.

    본 테스트는 AST 를 사용해 모든 함수/메서드 내부에서
    `import subprocess` 노드를 0건으로 박제한다. (top-level 은 허용)
    zutf-8)encoding)filenamec                  ,    e Zd Zd Zd ZeZeZ fdZy)`test_regression_2_no_local_subprocess_import_in_functions.<locals>._LocalSubprocessImportVisitorc                    g | _         y N)_func_stack)selfs    r9   __init__zitest_regression_2_no_local_subprocess_import_in_functions.<locals>._LocalSubprocessImportVisitor.__init__f   s
    *,Dr;   c                    | j                   j                  |j                         | j                  |       | j                   j	                          y rJ   )rK   r%   namegeneric_visitr   )rL   nodes     r9   _visit_functionzptest_regression_2_no_local_subprocess_import_in_functions.<locals>._LocalSubprocessImportVisitor._visit_functioni   s:    ##DII.t$  "r;   c                    | j                   sy |j                  D ]<  }|j                  dk(  sj                  | j                   d   |j                  f       > y )N
subprocess)rK   namesrO   r%   lineno)rL   rQ   alias	offenderss      r9   visit_Importzmtest_regression_2_no_local_subprocess_import_in_functions.<locals>._LocalSubprocessImportVisitor.visit_Importq   sR    ## J::-$$d&6&6r&:DKK%HIJr;   N)__name__
__module____qualname__rM   rR   visit_FunctionDefvisit_AsyncFunctionDefrZ   )rY   s   r9   _LocalSubprocessImportVisitorrH   e   s!    	-	#
 ,!0	Jr;   r`   )==)z%(py0)s == %(py3)srY   )r>   r@   uw   dispatch/__init__.py 함수 내부에서 local `import subprocess` 가 재발했다 (UnboundLocalError 재현 위험): u@   . top-level import subprocess (line 30) 만 사용해야 한다.z
>assert %(py5)sr   N)r   	read_textastparser   NodeVisitorvisitr   r    r!   r"   r#   r$   r'   r(   r)   )sourcetreer`   rB   r,   @py_format4r3   rY   s          @r9   9test_regression_2_no_local_subprocess_import_in_functionsrj   T   s     $$g$6F99Vc-&89D')IJ J& "#))$/ 9?  	9   	  	    	    	    	;;D+ FI	I   	 r;   c           	     &   d}d}t        | ||      }|t        u }|smt        j                  d|fd|t        f      dt	        j
                         v st        j                  t               rt        j                  t               nddt	        j
                         v st        j                  |       rt        j                  |       ndt        j                  |      t        j                  |      t        j                  |      dt	        j
                         v st        j                  t              rt        j                  t              nddz  }t        j                  d	      d
z   d|iz  }t        t        j                  |            dx}x}x}}y)u  dispatch 모듈에서 노출되는 subprocess 는 stdlib subprocess 와 동일해야 한다.

    함수 내부에 local import 가 잠입하면 모듈 attribute 자체는 영향이
    없지만, 모듈 attribute 와 stdlib subprocess 의 동일성을 박제하면
    누군가 실수로 top-level `import subprocess` 를 alias 로 바꾸거나
    제거했을 때 빠르게 잡힌다.
    rT   N)is)zB%(py7)s
{%(py7)s = %(py0)s(%(py1)s, %(py3)s, %(py5)s)
} is %(py9)sgetattrr:   _stdlib_subprocess)r>   r?   r@   r   r   r   uy   dispatch.subprocess 가 stdlib subprocess 모듈과 다르다 — top-level import subprocess (line 30) 를 확인할 것z
>assert %(py11)sr   )rm   rn   r   r    r!   r"   r#   r$   r'   r(   r)   )r:   rB   r-   @py_assert6@py_assert8@py_format10@py_format12s          r9   ?test_regression_3_subprocess_resolves_to_stdlib_at_module_levelrs      sR    %1 $ 7?L$7 7;MM   7;M                #    #    %1    37    8      <N    <N   	B     r;   )__doc__
__future__r   builtinsr!   _pytest.assertion.rewrite	assertionrewriter   rc   importlib.utilr   rT   rn   r   pathlibr   pytest__file__resolve
_TEST_FILEparents	WORKSPACEr   fixturer:   rC   rj   rs    r;   r9   <module>r      s   8 #   
  ' 
   (^##%
q!	J&6 h  (*Zr;   