
    i                     j    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mZ ddlm	Z	 dZ
e
 dZ G d d      Zy)	u  M-21 Persistent Shell — 상태 유지 셸 유틸리티.

subprocess.Popen으로 단일 셸 프로세스를 유지하며 CWD/환경변수 상태를 이어갑니다.
sentinels 패턴(echo "SENTINEL"$?)으로 exit code를 감지합니다.

Usage:
    from utils.persistent_shell import PersistentShell

    with PersistentShell() as sh:
        sh.run("cd /tmp")
        sh.run("export MY_VAR=hello")
        out, code = sh.run("echo $MY_VAR")  # ("hello", 0)
        cwd = sh.get_cwd()
    N)TracebackType)Optional__SHELL_DONE__z_{tag}_EXIT_c            	           e Zd ZdZddededdfdZdeddfdZd	ededefd
Zddede	e   de
eef   fdZdefdZdede	e   fdZddZddZde	ee      de	e   de	e   ddfdZy)PersistentShelluo   단일 셸 프로세스를 유지하며 run() 호출마다 상태(CWD, 환경변수)가 이어지는 클래스.shelltimeoutreturnNc           	         || _         || _        d | _        d| _        t	        j
                  | j                   gt        j                  t        j                  t        j                  dt        j                  j                               | _        y )NFr   )stdinstdoutstderrbufsizeenv)r   r	   _proc_closed
subprocessPopenPIPESTDOUTosenvironcopy)selfr   r	   s      G/home/jay/workspace/.worktrees/task-2116-dev1/utils/persistent_shell.py__init__zPersistentShell.__init__    sa    
8<
%%ZZL//??$$

!

    datac                    | j                   | j                   j                  t        d      | j                   j                  j                  |j	                  d             | j                   j                  j                          y )NzShell process is not runningutf-8)r   r   RuntimeErrorwriteencodeflush)r   r   s     r   _writezPersistentShell._write.   s^    ::!1!1!9=>>

t{{734

 r   sentinelc           	         | j                   | j                   j                  yd}t        j                         |z   }|j	                  d      }	 |t        j                         z
  }|dk  rnt        j
                  | j                   j                  gg g t        |d            \  }}}|rEt        j                  | j                   j                  j                         d      }	|	sn||	z  }||v rn|j                  dd      S )	uB   sentinel 문자열이 나타날 때까지 stdout을 읽어 반환. r   r    r   g?i   replace)errors)r   r   time	monotonicr#   selectminr   readfilenodecode)
r   r&   r	   bufdeadlinesentinel_bytes	remainingrlist_chunks
             r   _read_untilzPersistentShell._read_until4   s    ::!2!2!:>>#g-!1 4>>#33IA~ --):):(;RSTWEXYKE1a

 1 1 8 8 :DAu!S(  zz')z44r   cmdc                    | j                   s| j                  y||n| j                  }|j                         }|j	                  d      rQ|j                  d      }	 t        |      dkD  rt        |d         nd}	 | j                  | d       d| _         d	|fS t        j                  t        j                         j                  
      }	 | j                  | d| d       | j#                  ||      }d}	g }
|j%                         D ]y  }||v rb|j'                  |      }|d| j                         }|r|
j)                  |       	 t        ||t        |      z   d j                               }	 n|
j)                  |       { dj+                  |
      j                         |	fS # t        $ r d}Y ;w xY w# t        $ r Y 6w xY w# t         $ r
 d| _         Y yw xY w# t        $ r d}	Y {w xY w)uE   명령을 실행하고 (출력, exit_code) 튜플을 반환합니다.N)r(   exit   )maxsplitr   
Tr(   )tagz
echo 'z'$?
r<   )r   r   r	   strip
startswithsplitlenint
ValueErrorr%   	Exception_SENTINEL_TPLformatuuiduuid4hexBrokenPipeErrorr9   
splitlinesindexappendjoin)r   r:   r	   eff_timeoutstrippedpartscoder&   raw	exit_codeoutput_lineslineidxprefixs                 r   runzPersistentShell.runI   s   <<4::-!(!4g$,, 99;v&NNAN.E(+E
Qs58}Ase2J'  DL: ''DJJL,<,<'=	KK3%xz78
 x5	"$NN$ 	&D4jj*ds))+ ''/" #Ds8})<)>$?$E$E$G HI %	& 		,'--/;;C      	DL	  " " !I"sH   F) =F; G (G!)F87F8;	GGGG!G/.G/c                 J    | j                  d      \  }}|j                         S )u.   현재 작업 디렉토리를 반환합니다.pwd)r]   rB   )r   outr7   s      r   get_cwdzPersistentShell.get_cwdy   s    %Qyy{r   keyc                     dt        j                         j                   d}| j                  d| d| d      \  }}|j	                         }||k(  s|dk(  r | j                  d| d      \  }}d	|v rd
S dS |S )u?   환경변수 값을 조회합니다. 미설정 시 None 반환.__ENV_SENTINEL___zecho "${z:-z}"r(   z[ -z "${z$+x}" ] && echo "UNSET" || echo "SET"UNSETN)rK   rL   rM   r]   rB   )r   rb   sentinel_keyr`   r7   valuechecks          r   get_envzPersistentShell.get_env~   s    ()9)9(:"=IcU"\N#>?Q		L ERKxx)C50U VWHE1"e+433r   c                    | j                   ryd| _         | j                  s	 | j                  j                  r$| j                  j                  j                          	 | j                  j                          | j                  j                  d       yy# t        $ r Y Dw xY w# t        $ r. 	 | j                  j                          Y y# t        $ r Y Y yw xY ww xY w)u$   셸 프로세스를 종료합니다.NTg      @)r	   )r   r   r   closerH   	terminatewaitkillr   s    r   rl   zPersistentShell.close   s    <<::!::##JJ$$**,

$$&

, "  
  JJOO%  s;   :B 6B$ 	B! B!$	C.C

	CCCCc                     | S N rp   s    r   	__enter__zPersistentShell.__enter__   s    r   exc_typeexc_valexc_tbc                 $    | j                          y rr   )rl   )r   ru   rv   rw   s       r   __exit__zPersistentShell.__exit__   s     	

r   )z	/bin/bashg      >@rr   )r
   N)r
   r   )__name__
__module____qualname____doc__strfloatr   r%   r9   r   tuplerF   r]   ra   rj   rl   rt   typeBaseExceptionr   ry   rs   r   r   r   r      s    y
c 
% 
4 
!3 !4 !5C 5% 5C 5*.<s .<Xe_ .<c3h .<` 
3 8C= (4./ -( '	
 
r   r   )r}   r   r-   r   r+   rK   typesr   typingr   _SENTINEL_PREFIXrI   r   rs   r   r   <module>r      sB    
       $ #$N3H Hr   