
    5pj9                    t   S SK r S SKrS SKrS SKrS SKrS SKJr  S SKJr  S SK	J
r
Jr  S SKJr  S SKJr  S SKJrJrJrJrJrJrJr  S SKrSS	KJr  SS
KJrJrJrJr  SSK J!r!  SSK"J#r#J$r$J%r%  SSK&J'r'J(r(  SSK)J*r*J+r+  SSK,J-r-  \+R\                  " \/5      r0Sr1Sr2Sr3Sr4Sr5Sr6Sr7Sr8Sr9Sr:Sr;Sr<Sr=Sr>Sr?Sr@S \AS!\AS"\A4S# jrBS$\C\D-  \A-  S"\C4S% jrE\
 " S& S'5      5       rF\
 " S( S)5      5       rG\
 " S* S+5      5       rH " S, S-5      rIS.\A\\A   -  S/\JS-  S"\K\A\4   4S0 jrLS1\R                  S"\\K   4S2 jrNS1\R                  S"S4S3 jrO " S4 S55      rP " S6 S75      rQ " S8 S95      rR " S: S;5      rSS<\'S"\A4S= jrTS<\'S \AS>\K\A\4   S-  S?\K\A\4   S-  S@\\!   S-  SA\C\D-  \A-  S-  SB\JSC\AS"\U\V\A   \K\A\4   \K\A\4   \\!   4   4SD jrWS<\'SE\(SF\AS-  S"\K\A\4   4SG jrXSSH.S<\'SI\ASF\AS-  S"\(4SJ jjrYSSH.S<\'SK\ASF\AS-  S"\P4SL jjrZSM\ASF\AS-  S"\U\A\AS-  4   4SN jr[SE\(S"\A4SO jr\SSPSQ.S<\'SR\ASF\AS-  SS\CS"\A4
ST jjr]g)U    N)ThreadPoolExecutor)contextmanager)	dataclassfield)Path)	token_hex)AnyBinaryIOCallableIteratorListLiteraloverload   )	constants)
CachedHostdelete_pool_cacheread_pool_cachesave_pool_cache)Volume)HfHubHTTPErrorSandboxCommandErrorSandboxError)HfApiJobInfo)	get_tokenlogging)parse_durationi?  z
hf-sandboxzhf-sandbox-mode	dedicatedpoolzhf-sandbox-poolzhf-sandbox-noncezpython:3.12iX  24h2   .)	COMPLETEDERRORDELETEDCANCELED   z/.hf-sbx-servera#  set -e
d=/tmp/.sbx-server
if command -v wget >/dev/null 2>&1; then wget -q -O "$d" "$SBX_SERVER_URL"
elif command -v curl >/dev/null 2>&1; then curl -fsSL -o "$d" "$SBX_SERVER_URL"
else cp "$SBX_SERVER_MOUNT/sbx-server" "$d"; fi
chmod +x "$d"
unset SBX_SERVER_URL SBX_SERVER_MOUNT
exec "$d"
hf_tokennoncereturnc                     [         R                  " U R                  5       SU 3R                  5       [        R                  5      R                  5       $ )a%  Derive the per-sandbox auth token from the user's HF token and the sandbox nonce.

Stateless: any machine holding the same HF token can recompute it from the
nonce stored in the job's labels, so `Sandbox.connect(job_id)` needs no local state.
The HF token itself is never sent to the sandbox.
zhf-sandbox:)hmacnewencodehashlibsha256	hexdigest)r)   r*   s     T/var/www/html/pdf-tiff/venv/lib/python3.13/site-packages/huggingface_hub/_sandbox.py_derive_sandbox_tokenr4   ^   s;     88HOO%UG'<'C'C'Ew~~V``bb    durationc                 d    [        U [        [        45      (       a  [        U 5      $ [        U 5      $ )zBParse a duration like 300, "300s", "10m", "2h", "1d" into seconds.)
isinstanceintfloatr   )r6   s    r3   _duration_to_secsr;   h   s(    (S%L))8}(##r5   c                       \ rS rSr% Sr\S-  \S'   \\S'   \\S'   Sr\S-  \S'   Sr	\
\S	'   S
r\\S'   \S\
4S j5       rS\4S jrSrg)SandboxCommandResulto   z?Result of a command executed in a sandbox with [`Sandbox.run`].N	exit_codestdoutstderrsignalF	timed_outr   duration_msr+   c                      U R                   S:H  $ Nr   )r?   selfs    r3   okSandboxCommandResult.okz   s    ~~""r5   c                     [        U R                  5      S::  a  U R                  OU R                  S S S-   nSU R                   SU< SU R                   S3$ )NP   M   z...zSandboxCommandResult(exit_code=z	, stdout=z, duration_ms=))lenr@   r?   rD   )rH   outs     r3   __repr__SandboxCommandResult.__repr__~   sW     -3dkkSb9IE9Q00@	#P^_c_o_o^ppqrrr5    )__name__
__module____qualname____firstlineno____doc__r9   __annotations__strrB   rC   boolrD   propertyrI   rQ   __static_attributes__rS   r5   r3   r=   r=   o   sb    ITzKKFC$JItK#D # #s# sr5   r=   c                       \ rS rSr% Sr\\S'   \\\   -  \S'   \	" SSS9r
S\S'   S	r\S	-  \S
'   S	r\S	-  \S'   Sr\\S'   S	r\S	-  \S'   SS jrSrg	)SandboxProcess   aq  A background process started in a sandbox with [`Sandbox.run`]`(..., background=True)`.

List a sandbox's processes with [`Sandbox.processes`] and stop one with [`SandboxProcess.kill`].
Completed processes stay in the listing until the sandbox is deleted, so `running` and
`exit_code` tell whether a process is still alive or already exited (as of when it was listed).
pidcmdF)reprcompareSandbox_sandboxNtagstarted_at_msTrunningr?   c                 V    U R                   R                  SSU R                   35        g)z:Terminate the background process (idempotent server-side).DELETEz/processes/N)rf   _requestra   rG   s    r3   killSandboxProcess.kill   s!    x;txxj)ABr5   rS   r+   N)rT   rU   rV   rW   rX   r9   rY   rZ   r   r   rf   rg   rh   ri   r[   r?   rm   r]   rS   r5   r3   r_   r_      si     
H	tCy  UE:Hi:Ct $M3:$GT IsTz Cr5   r_   c                   j    \ rS rSr% Sr\\S'   \\S'   \S   \S'   \\S'   Sr	\S-  \S	'   S
r
\\S'   Srg)	FileEntry   z%A file or directory inside a sandbox.namepath)filedirsymlinktypesizeNmtime_ms moderS   )rT   rU   rV   rW   rX   rZ   rY   r   r9   rz   r|   r]   rS   r5   r3   rq   rq      s7    /
I
I
*
++
IHcDjD#Nr5   rq   c            	          \ rS rSrSrSrSrSrS!S jrS	\	S\
4S
 jrS"S	\	S\	S\	4S jjrS#S	\	S\	\
-  \-  S\	S-  SS4S jjrS#S\	\-  S	\	S\	S-  SS4S jjrS	\	S\	\-  SS4S jrS\S\\\\4      4S jrS\\   S\\/\4   S\\   4S jrS	\	S\S\\
   4S jrS	\	S\
S\	S-  SS4S jrS	\	S\\   4S jrS	\	S\4S jrS	\	S\4S jrS$S	\	S\SS4S jjr S	\	SS4S jr!S r"g)%SandboxFiles   u-  Filesystem operations inside a sandbox, available as [`Sandbox.files`].

In shared (pool) mode, paths are rooted at the sandbox's private home — the
only place its code can write — so a leading `/` is taken relative to that
home. In dedicated mode, paths are absolute on the container filesystem.
i          r+   Nc                     Xl         g Nrf   rH   sandboxs     r3   __init__SandboxFiles.__init__   s    r5   rt   c                     U R                  U5      R                  nX R                  :  a   SR                  U R	                  X5      5      $ U R
                  R                  SSSU0S9nUR                  $ )z=Read a file from the sandbox and return its content as bytes.r5   GET/files/readrt   params)statry   PARALLEL_THRESHOLDjoin_read_rangesrf   rl   content)rH   rt   ry   responses       r3   readSandboxFiles.read   sg    yy##)))88D--d9::==))%PT~)Vr5   encodingc                 B    U R                  U5      R                  U5      $ )z@Read a file from the sandbox and return its content as a string.)r   decode)rH   rt   r   s      r3   	read_textSandboxFiles.read_text   s    yy%%h//r5   datar|   c                 >   [        U[        5      (       a  UR                  5       nO%[        U[        5      (       d  UR	                  5       n[        U5      U R                  :  a  U R                  XU5        gSU0nUb  X4S'   U R                  R                  SSXBS9  g)zHWrite content to a file in the sandbox (parent directories are created).Nrt   r|   PUT/files/writer   r   )
r8   rZ   r/   bytesr   rO   r   _write_rangesrf   rl   )rH   rt   r   r|   r   s        r3   writeSandboxFiles.write   s    dC  ;;=DD%((99;Dt9t...t40$!6NunVRr5   
local_pathc                 *   [        U5      R                  5       R                  nX@R                  :  a+  U R	                  U[        U5      R                  5       U5        g[        US5       nU R                  X%US9  SSS5        g! , (       d  f       g= f)z#Upload a local file to the sandbox.Nrb)r|   )r   r   st_sizer   r   
read_bytesopenr   )rH   r   rt   r|   ry   fs         r3   uploadSandboxFiles.upload   sr    J$$&..)))tT*%5%@%@%BDI*d#qJJtTJ* $##s   *B
Bc                     U R                  U5      R                  nX0R                  :  a?  [        US5       nU R	                  X5       H  nUR                  U5        M     SSS5        gU R                  R                  SSSU0S9 n[        US5       nUR                  SS9 H  nUR                  U5        M     SSS5        SSS5        g! , (       d  f       g= f! , (       d  f       N(= f! , (       d  f       g= f)	z!Download a file from the sandbox.wbNr   r   rt   r   r   )
chunk_size)	r   ry   r   r   r   r   rf   _stream
iter_bytes)rH   rt   r   ry   r   partr   chunks           r3   downloadSandboxFiles.download   s    yy##)))j$'1 --d9DGGDM : ( ]]""5-"OS[j$'1%00K0HEGGEN I ( PO	 (' '' POs/   *CC/(C<C/
C
C,	(C//
C=ry   c           	      x    U R                   n[        SX5       Vs/ s H  o3[        X!U-
  5      4PM     sn$ s  snf rF   )PARALLEL_CHUNK_SIZErangemin)rH   ry   r   offsets       r3   _rangesSandboxFiles._ranges   s:    ((BG4BWXBWU6M23BWXXXs   7itemsfnc                     [        U R                  [        U5      5      n[        U5       n[	        UR                  X!5      5      sSSS5        $ ! , (       d  f       g= f)zRun `fn(item)` over items concurrently.

All workers share the sandbox's `httpx.Client`, which is thread-safe and pools
connections, so parallel transfers fan out over several streams at once.
N)r   PARALLEL_MAX_WORKERSrO   r   listmap)rH   r   r   workersexecutors        r3   	_parallelSandboxFiles._parallel   sA     d//U<(HR/0 )((s   A
Ac                    ^ ^ S[         [        [        4   S[        4UU 4S jjnT R                  T R	                  U5      U5      $ )Nrngr+   c                 `   > U u  pTR                   R                  SSTXS.S9nUR                  $ )Nr   r   )rt   r   lengthr   )rf   rl   r   )r   r   r   r   rt   rH   s       r3   fetch(SandboxFiles._read_ranges.<locals>.fetch   s=     NF}}--}df-_ . H ###r5   )tupler9   r   r   r   )rH   rt   ry   r   s   ``  r3   r   SandboxFiles._read_ranges   s?    	$uS#X 	$5 	$ 	$ ~~dll40%88r5   c                    ^ ^^^ S[         [        [        4   SS 4UUUU 4S jjnT R                  T R                  [	        T5      5      U5        g )Nr   r+   c           	      j   > U u  pTUS.nTb  TUS'   TR                   R                  SSUTXU-    S9  g )N)rt   r   r|   r   r   r   rf   rl   )r   r   r   r   r   r|   rt   rH   s       r3   push(SandboxFiles._write_ranges.<locals>.push  sK     NF.2f%EF!%vMM""5.QUV\hn_nQo"pr5   )r   r9   r   r   rO   )rH   rt   r   r|   r   s   ```` r3   r   SandboxFiles._write_ranges  sD    	qeCHo 	q$ 	q 	q 	t||CI.5r5   c                     U R                   R                  SSSU0S9nUR                  5       S    Vs/ s H  n[        S0 UD6PM     sn$ s  snf )z List a directory in the sandbox.r   z/files/listrt   r   entriesrS   )rf   rl   jsonrq   )rH   rt   r   entrys       r3   r   SandboxFiles.list  sO    ==))%PT~)V08	0JK0Ju	"E"0JKKKs   Ac                 n    U R                   R                  SSSU0S9n[        S0 UR                  5       D6$ )z3Get metadata of a file or directory in the sandbox.r   z/files/statrt   r   rS   )rf   rl   rq   r   )rH   rt   r   s      r3   r   SandboxFiles.stat  s6    ==))%PT~)V+8==?++r5   c                 x     U R                  U5        g! [         a  nUR                  S:X  a   SnAge SnAff = f)z+Check whether a path exists in the sandbox.Ti  NF)r   r   status_code)rH   rt   es      r3   existsSandboxFiles.exists  s7    	IIdO 	}}#	s    
9449	recursivec                 Z    SU0nU(       a  SUS'   U R                   R                  SSUS9  g)z*Delete a file or directory in the sandbox.rt   1r   rk   z/files/deleter   Nr   )rH   rt   r   r   s       r3   deleteSandboxFiles.delete  s1    $"%F;xHr5   c                 >    U R                   R                  SSSU0S9  g)z0Create a directory (and parents) in the sandbox.POSTz/files/mkdirrt   r   Nr   )rH   rt   s     r3   mkdirSandboxFiles.mkdir&  s    v~vtnMr5   r   )r   re   r+   N)zutf-8r   )F)#rT   rU   rV   rW   rX   r   r   r   r   rZ   r   r   r   r
   r   r   r   r   r9   r   r   r   r	   r   r   r   r   rq   r   r   r[   r   r   r   r]   rS   r5   r3   r~   r~      s    ))      0c 0S 0s 0S# SS5[8%; S3: SY] S+t +3 +cDj +TX +#S #cDj #T #YC YDsCx$9 Y1tCy 1hucz.B 1tCy 19 9C 9DK 96# 6U 6#* 6 6L Li L
, , ,
3 4 I3 I4 ID IN# N$ Nr5   r~   rb   shellc                     USL a   [        U [        5      (       d  [        S5      eUSL a   [        U [        5      (       a  [        S5      eSU 0nUb  XS'   U$ )zQBuild the `cmd`/`shell` part of an `/exec` payload, validating their consistency.TzCshell=True requires `cmd` to be a shell command string, not a list.FzRshell=False requires `cmd` to be an argv list (e.g. ['echo', 'hi']), not a string.rb   r   )r8   rZ   
ValueError)rb   r   payloads      r3   _exec_payloadr   +  s\    }ZS11^__~*S#..mnn$clG Nr5   r   c              #      #    U R                  5        H=  nU(       d  M  [        R                  " U5      nUR                  S5      S:w  d  M9  Uv   M?     g7f)zJIterate NDJSON events from a streaming response, skipping keepalive pings.eventpingN)
iter_linesr   loadsget)r   liner   s      r3   _iter_eventsr   7  sB     ##%

4 99W'K &s   AA	Ac                     U R                  5          U R                  5       S   n[	        SU R
                   SU 3U R
                  S9e! [         a    U R                  SS n N?f = f)zQRead the error body and raise a SandboxError (works for streaming responses too).errorNi  zSandbox API error (z): )r   )r   r   	Exceptiontextr   r   )r   messages     r3   _raise_for_statusr   A  sl    MMO&--/'* ,X-A-A,B#gYO]e]q]q
rr  &--%&s   A	 	A%$A%c                   <   \ rS rSrSrSSS.S\S\S\S	-  S
\S\S\S\S\S\SS	4S jjr\	SSS.S\
S\S\S\S\S\SS 4S jj5       r\S\S	-  4S j5       rS\S\S\R                  4S jr\S\S\S\\R                     4S j5       rSS jrSS jrS\SS	4S jrSrg	)_SandboxServeriK  u5  HTTP transport to one `sbx-server` instance — a dedicated job or a shared host.

Owns the `httpx.Client`, the base URL and the auth headers.
In dedicated mode a server is paired 1:1 with its [`Sandbox`
In pool mode one server (one host job) is shared by many sandboxes, and `live`/`capacity` track packing.

   r   )max_connectionscapacityjob_idownerimageNbase_urlr*   sandbox_tokenapir   r  r+   c       	            Xl         X l        X0l        X@l        XPl        Xpl        [        U5      U l        X`l        Xl	        SU l
        SU l        [        R                  " SU R                   3US.[        R                  " XS9SS9U l        g )Nr   TBearer AuthorizationzX-Sandbox-Token)r   max_keepalive_connections)headerslimitsfollow_redirects)r  r  _imager  r*   _api_effective_token_auth_token_sandbox_tokenr  liveverifiedhttpxClientLimits_client)
rH   r  r  r  r  r*   r  r  r   r  s
             r3   r   _SandboxServer.__init__S  s     
  
	+C0+ 	  ||#*4+;+;*<!=#0 <<k!
r5   jobc                    U " UR                   UR                  R                  UR                  =(       d    UR                  [        U5      UUUUUS9	$ )zIBuild a server from a freshly fetched job (reads its exposed server URL).	r  r  r  r  r*   r  r  r   r  )idr  rs   docker_imagespace_id_find_server_url)clsr  r*   r  r  r   r  s          r3   from_job_SandboxServer.from_job{  sL     66))..""2cll%c*'+

 
	
r5   c                     U R                   $ r   )r  rG   s    r3   r  _SandboxServer.image  s    {{r5   methodrt   c                     UR                  S[        R                  " SSS95      nU R                  R                  " XR
                  U-   4SU0UD6nUR                  S:  a  [        U5        U$ )z@Request to the in-job server. Raises SandboxError on API errors.timeoutg      N@      $@connect  )popr  Timeoutr  requestr  r   r   rH   r(  rt   kwargsr*  r   s         r3   r1  _SandboxServer.request  s`    **YdD(IJ<<''0D`g`Y_`3&h'r5   c              +      #    UR                  S[        R                  " SSS95      nU R                  R                  " XR
                  U-   4SU0UD6 nUR                  S:  a  [        U5        Uv   SSS5        g! , (       d  f       g= f7f)zJStreaming request to the in-job server. Raises SandboxError on API errors.r*  g     Q@r+  r,  r.  N)r/  r  r0  r  streamr  r   r   r2  s         r3   r6  _SandboxServer.stream  sr      **YdD(IJ\\  )=YwYRXY]e##s*!(+N ZYYs   AB A=4	B=
BBc                 8    U R                   R                  5         g r   )r  closerG   s    r3   r9  _SandboxServer.close  s    r5   c                 `    U R                   R                  U R                  U R                  S9  g )Nr  	namespace)r  
cancel_jobr  r  rG   s    r3   r>  _SandboxServer.cancel_job  s!    		DKK4::Fr5   start_timeoutc           
         [         R                   " 5       U-   nSn[         R                   " 5       U:  Gao   U R                  R                  U R                  S-   [        R
                  " S5      S9nUR                  S:X  a  g [         R                   " 5       U-
  S:  a  [         R                   " 5       nU R                  R                  U R                  U R                  S9nUR                  R                  [        ;   ar  [        U R                  U R                  U R                  S	9n[!        S
U R                   SUR                  R                   SUR                  R"                   SU 35      e[         R$                  " S5        [         R                   " 5       U:  a  GMo  [!        S
U R                   SUS S35      e! [        R                   a     GNUf = f)zMPoll /health until the server answers; fail fast (with logs) if the job dies.g        z/healthg      @)r*     Ng       @r<  r=  zSandbox job z$ terminated during startup (status: z, message: ).g333333?z did not become ready within z.0fzs.)timer  r   r  r  r0  r   RequestErrorr  inspect_jobr  r  statusstage_TERMINAL_STAGES_tail_job_logsr   r   sleep)rH   r@  deadlinelast_job_checkr   r  logss          r3   
wait_ready_SandboxServer.wait_ready  s   99;.iikH$<<++DMMI,Eu}}]`Oa+b''3. / yy{^+c1!%ii++4;;$**+U::##'77)$))T[[DJJWD&&t{{m 4$$'JJ$4$4#5[ASAS@TTVW[V\^  JJt! iikH$" \$++6STabeSffhijj %% s   AG GG)r  r  r  r  r  r  r  r  r  r*   r  r  ro   )rT   rU   rV   rW   rX   rZ   r   r9   r   classmethodr   r$  r\   r  r  Responser1  r   r   r6  r9  r>  r:   rP  r]   rS   r5   r3   r   r   K  so   "  "&
 &
 	&

 Tz&
 &
 &
 &
 &
 &
 &
 
&
P   "
 
 	

 
 
 
 
 

 
. sTz  c  5>>  S  (5>>:R  Gk k$ kr5   r   c            	           \ rS rSrSr\SSS\S\S   4S j5       r\SS	S\S\/ S4   4S
 j5       rSSS\S\S   4S jrSr	g)_KillMethodi  z>Lets `kill` work both as a classmethod and an instance method.instanceNr  r+   ).Nc                     g r   rS   rH   rV  r  s      r3   __get___KillMethod.__get__  s    KNr5   re   c                     g r   rS   rX  s      r3   rY  rZ    s    ORr5   Sandbox | Nonec           	      t   ^ Ub  UR                   $ S S S.S[        S[        S -  S[        S -  SS 4U4S jjjnU$ )Nr=  token
sandbox_idr=  r_  r+   c                B   > TR                  XUS9R                  5         g )Nr^  )r-  rm   )r`  r=  r_  r  s      r3   rm   !_KillMethod.__get__.<locals>.kill  s    MM*MGLLNr5   )_killrZ   )rH   rV  r  rm   s     ` r3   rY  rZ    sV    >>!=AW[ 	OS 	Od
 	O#PT* 	O`d 	O 	O r5   rS   )
rT   rU   rV   rW   rX   r   rx   r   rY  r]   rS   r5   r3   rU  rU    sm    HNNTNhy6IN NR	R$R8BH;MR R 0  (9BU r5   rU  c                   0   \ rS rSrSrS\S\S\S-  S\S\S	S4S
 jr\	\
4S\SSSSSSSS.	S\S\S\\-  \-  S-  S\\\4   S-  S\\\4   S-  S\\   S-  S\S-  S\S\S\S-  S	S 4S jjj5       r\	SSS.S\S\S-  S\S-  S	S 4S jj5       r\" 5       rSAS jrSAS jrSBS jrSAS  jr\S!S!S!S!S!S!S!S!S!S".	S#\\\   -  S$\S-  S\\\4   S-  S%\S-  S&\S-  S'\S-  S(\\/S4   S-  S)\\/S4   S-  S*\S+\S   S	\4S, jj5       r\S!S!S!S-.S#\\\   -  S$\S-  S\\\4   S-  S%\S-  S+\S.   S	\4S/ jj5       rSSSSSSSS.SS".	S#\\\   -  S$\S-  S\\\4   S-  S%\S-  S&\S-  S'\S-  S(\\/S4   S-  S)\\/S4   S-  S*\S+\S	\\-  4S0 jjrS	\\   4S1 jr \!S	\S-  4S2 j5       r"\!S	\S-  4S3 j5       r#SCS4S5.S6\\-  S7\S8\S	\4S9 jjjr$\!S	\\\4   4S: j5       r%S	\4S; jr&S<\S=\S	\'RP                  4S> jr)\*S<\S=\S	\+\'RP                     4S? j5       r,S@r-g)Dre   i  a  An isolated cloud machine running on Hugging Face Jobs.

Create a dedicated one with [`Sandbox.create`] (one job per sandbox), or get many cheap shared ones from a [`SandboxPool`].
Reattach to a running sandbox from anywhere with [`Sandbox.connect`]. Use as a context manager to terminate it on exit:

```python
>>> from huggingface_hub import Sandbox
>>> with Sandbox.create(image="python:3.12") as sbx:
...     print(sbx.run("python --version").stdout)
```
r  serverlocal_idNowns_sandboxowns_serverr+   c                    Xl         X l        X0l        Uc  SOSU 3U l        X@l        XPl        S U l        SU l        [        U 5      U l	        g )Nz/v1/v1/sandboxes/F)
r  _server	_local_id
_base_path_owns_sandbox_owns_server_on_kill_killedr~   files)rH   r  re  rf  rg  rh  s         r3   r   Sandbox.__init__  sT     ! $,#3%>(9T * (<@!$'
r5   	cpu-basicF      ^@)	flavoridle_timeoutenvsecretsvolumesr=  forward_hf_tokenr@  r_  r  rv  rw  rx  ry  rz  r=  r{  r@  r_  c       	            [        U
S9n[        U5      n[        S5      n[        X5      n[	        UUUUUUUUS9u  nnnnUR                  UUUUU[        [        S[        [        [        U0U=(       d    S[        /US9
nSn [        R                  UUUU[        R                  S-   S9nUR!                  U	5        U " UR&                  USSSS9$ ! ["         a     UR%                  UR&                  UR(                  R*                  S	9  O<! ["         a/  n[,        R/                  S
UR&                   SU 35         SnAOSnAff = fUb  UR1                  5         e f = f)u;  Create a dedicated sandbox (one HF Job) and block until it is ready (~7s on cpu-basic).

Each sandbox is a full isolated VM, so this is the right choice for GPU
workloads or untrusted code. To fan out many cheap CPU sandboxes instead, use
[`SandboxPool`].

The job runs with a fixed 24h maximum lifetime; `idle_timeout` is the real
keeper — an idle sandbox shuts itself down well before that.

Args:
    image (`str`, *optional*, defaults to `"python:3.12"`):
        Any Docker image with `/bin/sh` (Docker Hub or `hf.co/spaces/...`).
    flavor (`str`, *optional*, defaults to `"cpu-basic"`):
        Hardware flavor, e.g. `"cpu-basic"`, `"a10g-small"`. See `hf jobs hardware`.
    idle_timeout (`int` or `float` or `str`, *optional*, defaults to `600`):
        Auto-shutdown after this much inactivity (no API calls, no running
        processes). Defaults to 10 minutes; pass `None` to disable.
    env (`dict[str, Any]`, *optional*):
        Environment variables available in the sandbox.
    secrets (`dict[str, Any]`, *optional*):
        Secret environment variables (encrypted server-side).
    volumes (`List[Volume]`, *optional*):
        HF repos/buckets to mount, see [`Volume`].
    namespace (`str`, *optional*):
        User or org namespace to run under (defaults to current user).
    forward_hf_token (`bool`, *optional*, defaults to `False`):
        If True, your HF token is injected as `HF_TOKEN` (opt-in).
    start_timeout (`float`, *optional*, defaults to `120.0`):
        Max seconds to wait for the sandbox to become ready.
    token (`str`, *optional*):
        HF token override.

The image only needs `/bin/sh`. The sandbox server is downloaded at startup with
`wget`/`curl` if available, otherwise read off an always-mounted server bucket (which
adds ~2-3s to cold start, so shipping `wget`/`curl` keeps it fast).
r_  r   rx  ry  rz  rw  r{  r  r   N
r  commandrx  ry  rv  r*  labelsrz  exposer=     r  r*   r  r  r   r<  zFailed to cancel sandbox job  after startup failure: Tr  re  rf  rg  rh  )r   r  r   r4   _bootstrap_job_specrun_jobSANDBOX_MAX_LIFETIMESANDBOX_LABEL
MODE_LABELMODE_DEDICATEDNONCE_LABELSANDBOX_SERVER_PORTr   r$  r~   r   rP  r   r>  r  r  rs   loggerwarningr9  )r#  r  rv  rw  rx  ry  rz  r=  r{  r@  r_  r  r)   r*   r  r  job_envjob_secretsjob_volumesr  re  r   s                         r3   createSandbox.create  s   f % #C("-h>5H%-'	6
2+{ kk(!3
NKQVW'4'(  
 +/	#,,+ , A AA E - F m, cffVd[_``  		dcff		G d!>svvhF^_`^abccd!		s6   9C 
E.D
	E

E%D>9E>EEr^  r`  c                   [        US9n[        X5      u  p[        U;   av  UR                  [        S5      u  pV[	        XEUS9n UR                  SS5      R                  5        Vs1 s H  oS   iM	     n	nXi;  a  [        SU SU S	35      e U " XUS
SS9$ UR                  XS9n
U
R                  =(       d    0 nUR                  [        5      nUR                  [        5      S:w  d  Uc  [        SU S[         S35      eUR                  [        5      [         :X  a  [        SU S[         S35      eU
R"                  R$                  S:w  a&  [        SU SU
R"                  R$                   S35      e['        [)        U5      U5      n[*        R-                  U
UUU[.        R0                  S-   S9nU " U
R2                  USS
SS9$ s  snf ! [         a    UR                  5         e f = f)z?Reattach to a running sandbox from anywhere, using only its id.r}  r   rC  r   /v1/sandboxesr  zSandbox z no longer exists on host r#   FTr  r<  r   NJob z is not a sandbox (missing 'z	' label).z` is a sandbox host, not a single sandbox. Connect to one of its sandboxes with id '<host_job_id>z<local_id>'.RUNNING is not running (status: rD  r  r  )r   _split_sandbox_idSHARED_ID_SEPsplit_connect_hostr1  r   r   r   r9  rG  r  r   r  r  r  	MODE_POOLrH  rI  r4   r  r   r$  r~   r   r  )r#  r`  r=  r_  r  host_job_idrf  re  itemexistingr  r  r*   r  s                 r3   r-  Sandbox.connectf  s     %  1* H
J&$.$4$4]A$F!K"3yIF39>>%3Y3^3^3`a3`4J3`a+&*=WXcWdde'fgg ,
 *hUZhlmmooZoE!r

;'::m$+u}j\1Mm_\efgg::j!Y.zl #33@/O  ::y(*5NszzO_O_N``bcdd-.>s.CUK(('(==A ) 
 cffVd\`aa9 b  s   "G &G4G G G'c                    U R                   (       a  g U R                  c  U R                  R                  5         O*U R                  R	                  SSU R                   35         SU l         U R                  b  U R                  U 5        U R                  (       a  U R                  R                  5         gg! [
         a/  n[        R                  SU R                   SU 35         SnAgSnAff = f)zTerminate the sandbox. Idempotent.

Dedicated sandboxes cancel their underlying job; shared sandboxes are
removed from their host (freeing a slot) while the host keeps running.
Nrk   rj  zFailed to kill sandbox : T)rq  rl  rk  r>  r1  r   r  r  r  rp  ro  r9  )rH   r   s     r3   rc  Sandbox._kill  s     <<	~~%'')$$X?O/PQ
 ==$MM$LL    	NN4TWWIRsCD	s   'B8 )B8 8
C1%C,,C1c                 \    U R                   (       a  U R                  R                  5         gg)zRelease the local HTTP client without terminating the sandbox. Idempotent.

No-op for pool sandboxes (the client belongs to the pool's host).
N)ro  rk  r9  rG   s    r3   r9  Sandbox.close  s"    
 LL  r5   c                     U $ r   rS   rG   s    r3   	__enter__Sandbox.__enter__      r5   c                 h    U R                   (       a  U R                  5         g U R                  5         g r   )rn  rm   r9  rH   exc_infos     r3   __exit__Sandbox.__exit__  s    IIKJJLr5   .)	r   rx  cwdr*  stdin	on_stdout	on_stderrcheck
backgroundrb   r   r  r*  r  r  r  r  r  c       	             g r   rS   )rH   rb   r   rx  r  r*  r  r  r  r  r  s              r3   runSandbox.run  s      #r5   )r   rx  r  Tc                    g r   rS   )rH   rb   r   rx  r  r  s         r3   r  r    s     r5   c       	         |   [        X5      nU(       a  X;S'   U(       a  XKS'   U
(       a<  U R                  SSUS9R                  5       n[        US   XR	                  S5      U S9$ Ub  X[S
'   Ub  XkS'   / n/ nS	nU R                  SSUS9 n[        U5       H  nUS   S:X  a&  UR                  US   5        Ub  U" US   5        M0  M2  US   S:X  a&  UR                  US   5        Ub  U" US   5        M_  Ma  US   S:X  d  Ml  [        US   SR                  U5      SR                  U5      UR	                  S5      UR	                  SS5      UR	                  SS5      S9nM     S	S	S	5        Uc  [        S5      eU	(       a*  UR                  S:w  d  UR                  (       a	  [        XS9eU$ ! , (       d  f       NO= f)u  Run a command in the sandbox and wait for it, streaming output live.

With `background=True` the command is started detached and `run` returns a
[`SandboxProcess`] immediately, without waiting for it to finish — handy for
servers and other long-running processes. List them later with [`Sandbox.processes`]
and stop one with [`SandboxProcess.kill`]. The streaming/wait-only options
(`timeout`, `stdin`, `on_stdout`, `on_stderr`, `check`) don't apply in that mode.

Args:
    cmd (`str` or `List[str]`):
        A shell command string (run with `/bin/sh -c`) or an argv list (exec'd directly).
    shell (`bool`, *optional*):
        Force the execution mode instead of inferring it from the type of `cmd`.
        `True` runs through `/bin/sh -c` and requires `cmd` to be a string; `False`
        exec's `cmd` directly and requires it to be an argv list. `None` (default)
        infers from the type. Set it explicitly to avoid the type-driven footgun (e.g.
        `["echo hi"]` being exec'd as a single program named `"echo hi"`).
    env (`dict[str, Any]`, *optional*):
        Extra environment variables for this command.
    cwd (`str`, *optional*):
        Working directory.
    timeout (`float`, *optional*):
        Kill the command (whole process group) after this many seconds.
    stdin (`str`, *optional*):
        Data to write to the command's stdin.
    on_stdout (`Callable[[str], None]`, *optional*):
        Callback invoked with stdout chunks as they arrive.
    on_stderr (`Callable[[str], None]`, *optional*):
        Callback invoked with stderr chunks as they arrive.
    check (`bool`, *optional*, defaults to `True`):
        If True, raise [`SandboxCommandError`] on non-zero exit.
    background (`bool`, *optional*, defaults to `False`):
        If True, start the command detached and return a [`SandboxProcess`] right
        away instead of waiting for it and returning a [`SandboxCommandResult`].

Returns: a [`SandboxCommandResult`] (with `exit_code`, `stdout`, `stderr`,
`duration_ms`), or a [`SandboxProcess`] when `background=True`.
rx  r  r   
/processesr   ra   rg   )ra   rb   rg   rf   Nr*  r  z/execr   r@   r   rA   exitr?   r{   rB   rC   FrD   r   )r?   r@   rA   rB   rC   rD   z%connection lost while running command)rb   result)r   rl   r   r_   r   r   r   appendr=   r   r   r?   rC   r   )rH   rb   r   rx  r  r*  r  r  r  r  r  r   r   stdout_partsstderr_partsr  r   r   s                     r3   r  r    s   h  + EN EN==G=DIIKD!d5ksZ^__!(I$G"$"$.2\\&'\8H%h/>X- ''f6 ,!%-0 -7^x/ ''f6 ,!%-0 -7^v-1"'"4!ww|4!ww|4$yy2"'))K"?$)IImQ$?F 0 9& >FGGf&&!+v/?/?%#==/ 98s   
A4F-A"F--
F;c                     U R                  SS5      R                  5       nU Vs/ s HH  n[        US   US   UR                  S5      UR                  S5      US   UR                  S5      U S	9PMJ     sn$ s  snf )
a  List the background processes of this sandbox.

Returns the processes started with [`Sandbox.run`]`(..., background=True)`; stop one
with [`SandboxProcess.kill`]. Completed processes stay listed (with `running=False` and
their `exit_code`) until the sandbox is deleted.
r   r  ra   rb   rg   rh   ri   r?   )ra   rb   rg   rh   ri   r?   rf   )rl   r   r_   r   )rH   r   ps      r3   	processesSandbox.processes3  s     }}UL1668 
  eHeHEE%LeeO4)%%, 
 	
 
s   AA7c                 .    U R                   R                  $ r   )rk  r  rG   s    r3   r  Sandbox.imageJ  s    ||!!!r5   c                 L    U R                   b  U R                  R                  $ S$ )zIFor a shared/pool sandbox, the job id of the host running it (else None).N)rl  rk  r  rG   s    r3   host_idSandbox.host_idN  s"     '+nn&@t||""JdJr5   zhttps://)schemeportrt   r  c                    U R                   R                  R                  SS5      S   nUR                  S5      (       a  UOSU-   nU U U R                   SU U 3$ )aL  Public URL that proxies through to a server running *inside* this sandbox.

Requests to the returned URL are forwarded by the in-job sandbox server to a
server you started in the sandbox on `port`, including WebSocket (`ws(s)://`)
upgrades and streamed responses. Pair it with [`proxy_headers`] for auth.

How the sandbox must listen on `port`:

- **Pool / shared sandbox**: it cannot bind a TCP port (Landlock), so bind a
  **unix socket** at `$SBX_PROXY_DIR/<port>.sock` (the `SBX_PROXY_DIR` env var
  is set in every sandbox). E.g. `uvicorn app:app --uds $SBX_PROXY_DIR/8000.sock`.
- **Dedicated sandbox**: bind a normal TCP port on `127.0.0.1:<port>`. (You can
  also expose the port directly via the job proxy without going through here.)

Args:
    port (`int` or `str`):
        The port (pool: the `<port>` of the unix socket) the inner server listens on.
    path (`str`, *optional*, defaults to `"/"`):
        Path on the inner server to point at, e.g. `"/ws"`.
    scheme (`str`, *optional*, defaults to `"https://"`):
        URL scheme to build the link with. Defaults to `"https://"`; pass
        `"wss://"` for a WebSocket client (the proxy is protocol-agnostic, so only
        the client-side scheme changes).

Returns:
    `str`: a URL like `https://<job_id>--49983.hf.jobs/v1/.../proxy/8000/ws` (or
    `wss://...` with `scheme="wss://"`).

Example:
    ```python
    >>> url = sandbox.proxy_url_for(8000, "/ws", scheme="wss://")
    >>> import websockets
    >>> async with websockets.connect(url, additional_headers=sandbox.proxy_headers) as ws:
    ...     await ws.send("hello")
    ```
z://r   /z/proxy/)rk  r  r  
startswithrm  )rH   r  rt   r  host_and_rests        r3   proxy_url_forSandbox.proxy_url_forU  sa    L --33E1=bAs++tt-(9tfMMr5   c                 b    SU R                   R                   3U R                   R                  S.$ )zPAuth headers to send with [`proxy_url_for`] requests (HF token + sandbox token).r	  r
  )rk  r  r  rG   s    r3   proxy_headersSandbox.proxy_headers  s0      't||'?'?&@A#||::
 	
r5   c                 @    SU R                   < SU R                  < S3$ )NzSandbox(id=z, image=rN   )r  r  rG   s    r3   rQ   Sandbox.__repr__  s    TWWKx

~Q??r5   r(  resourcec                 V    U R                   R                  " XR                  U-   40 UD6$ r   )rk  r1  rm  )rH   r(  r  r3  s       r3   rl   Sandbox._request  s%    ||##FOOh,FQ&QQr5   c              +      #    U R                   R                  " XR                  U-   40 UD6 nUv   S S S 5        g ! , (       d  f       g = f7fr   )rk  r6  rm  )rH   r(  r  r3  r   s        r3   r   Sandbox._stream  s9     \\  8)CNvNRZN ONNs   +A;	A
A	A)	rm  rq  rl  rp  rn  ro  rk  rr  r  ro   )r+   re   )r  ).rT   rU   rV   rW   rX   rZ   r   r[   r   rR  DEFAULT_IMAGEDEFAULT_IDLE_TIMEOUTr9   r:   dictr	   r   r   r  r-  rU  rm   rc  r9  r  r  r   r   r   r=   r  r_   r  r\   r  r  r  r  rQ   r  rS  rl   r   r   r   r]   rS   r5   r3   re   re     s   
( ( 	(
 *( ( ( 
(:  #ba "1E%))-'+ $!&$ baba 	ba
 EkC'$.ba #s(^d"ba c3h$&ba f$ba :ba ba ba Tzba 
ba baH AE[_ $b $bC$J $bcTXj $bdm $b $bP =D!.! 
 !%( #2525%(#49_# d{	#
 #s(^d"# 4Z# # Tz# SE4K(4/# SE4K(4/# # EN# 
# # 
 !%(49_ d{	
 #s(^d" 4Z DM 
  "%) $ 2626 [49_[ d{	[
 #s(^d"[ 4Z[ [ Tz[ SE4K(4/[ SE4K(4/[ [ [ 
	.[z
4/ 
. "sTz " " Kt K K(Nz (N#) (N3 (N (N^a (NT 
tCH~ 
 
@# @
Rs Rc R R c S x?W  r5   re   c                   r   \ rS rSrSr\4S\SSS\SSSSS.
S	\S
\S\	S\	S\	S-  S\S-  S\	\
-  \-  S-  S\S-  S\
S\S-  S\SS4S jjjr\SSS.S\S\S-  S\S-  SS 4S jj5       rS;S\	S\\   4S jjrS\SS.S\\\4   S-  S\	\
-  \-  S-  S\SS4S jjr\S\	4S  j5       r\S\	4S! j5       r\S\\   4S" j5       rS<S# jrS=S$ jrS<S% jrS<S& jrS>S( jrS)\S*   SS'4S+ jrS\4S, jrS<S- jrS.\	S\\    4S/ jr!S\ 4S0 jr"S1S*S\\\4   S2\	S-  SS34S4 jr#S5\$SS4S6 jr%S1\ SS4S7 jr&S<S8 jr'S<S9 jr(S:r)g)?SandboxPooli  a  A fleet of shared "host" jobs, each packing many landlock-isolated sandboxes.

One host is one billed HF Job (a VM); it runs the sandbox server and multiplexes
up to `sandboxes_per_host` lightweight sandboxes, isolated from each other by
uid + the Landlock LSM. This makes large fan-outs cheap (the VM cost is shared
across all its sandboxes) and fast (creating a sandbox is ~one proxy round-trip
once a host is warm). Best for many parallel CPU sandboxes such as RL rollouts;
for GPU or strong VM-level isolation between mutually-distrusting workloads, use
[`Sandbox.create`] instead.

The constructor pre-provisions `warm_up` hosts (default 1) and blocks until they are
ready; further hosts are then provisioned on demand as sandboxes are requested, and all
are torn down on `close()` (or when idle, via `idle_timeout`). The user never manages jobs:

```python
>>> from huggingface_hub import SandboxPool
>>> with SandboxPool(image="python:3.12", flavor="cpu-basic", warm_up=2) as pool:
...     boxes = [pool.create() for _ in range(100)]   # packed across the warm hosts
...     print(boxes[0].run("echo hi").stdout)
hi
```

`create()` makes **one** sandbox at a time: it reuses a host that still has free
capacity before booting a new one, so you grow on demand as work arrives. To avoid
a cold start on the first few calls, pre-provision hosts with `warm_up` (or
[`warm`]). Warm hosts are discovered via job labels, so reuse works **across
processes** too (a fresh pool with the same `image`/`flavor`/`name` attaches to
hosts an earlier run left behind):

```python
>>> pool = SandboxPool(image="python:3.12")
>>> sbx = pool.create()    # finds a warm host (here or in another process), else boots one
```
rt  r   Nru  F)
rv  sandboxes_per_hostwarm_up	max_hostsrs   rw  r=  r@  r_  _connect_moder  rv  r  r  r  rs   rw  r=  r@  r_  r  r+   c       
         ,   US:  a  [        S5      eUS:  a  [        S5      e[        U
S9U l        Xl        X l        X0l        X@l        XPl        Ub  UOS[        S5       3U l	        Xpl
        Xl        Xl        / U l        [        R                  " 5       U l        [        R                  " 5       U l        [        R                  " 5       U l        SU l        SU l        Xl        U(       + U l        [/        5       U l        U(       d  U R3                  5         gg)	u  Configure a pool and pre-provision `warm_up` hosts (blocks until they are ready).

Env/secrets are *not* set here: they belong to each sandbox and are passed to
`create(env=...)`, so sandboxes in the same pool can have different environments.

Args:
    image (`str`, *optional*, defaults to `"python:3.12"`):
        Docker image for the hosts (needs `/bin/sh`). All sandboxes in the
        pool share this image.
    flavor (`str`, *optional*, defaults to `"cpu-basic"`):
        Hardware flavor for the host jobs (e.g. `"cpu-basic"`).
    sandboxes_per_host (`int`, *optional*, defaults to `50`):
        How many sandboxes to pack per host (per VM density).
    warm_up (`int`, *optional*, defaults to `1`):
        How many hosts to pre-provision in the constructor (which blocks
        until they are ready), so an initial burst of `create()` calls doesn't pay
        a host cold start each. Existing warm hosts (from the cache / other processes)
        count towards it, so only the shortfall is booted; capped by `max_hosts`.
        Defaults to 1 (a single host).
    max_hosts (`int`, *optional*):
        Optional cap on the number of host jobs (a cost ceiling). When
        reached and all hosts are full, `create()` raises.
    name (`str`, *optional*):
        Pool name, used as the `hf-sandbox-pool` job label so the pool is
        discoverable (e.g. `hf sandbox pool ls`, `connect()`). `create()` reuses
        running hosts carrying this label (including from other processes) before
        booting new ones, so distinct names keep separate pools from sharing hosts.
        A random name is generated when omitted.
    idle_timeout (`int` or `float` or `str`, *optional*, defaults to `600`):
        Host idle timeout — a host shuts down once it has had no
        sandboxes for this long (a billing backstop). Each sandbox also has its
        own idle timeout, set at `create()`. Pass `None` to disable.
    namespace (`str`, *optional*):
        User or org namespace to run hosts under.
    start_timeout (`float`, *optional*, defaults to `120.0`):
        Max seconds to wait for a host to become ready.
    token (`str`, *optional*):
        HF token override.
r   z sandboxes_per_host must be >= 1.zwarm_up must be >= 1.r}  Nzpool-   F)r   r   r  r  rv  r  _warm_upr  r   rs   _idle_timeout
_namespace_start_timeout_hosts	threadingLock_lock_warmup_lock
_boot_lock_closed
_warmed_up_require_live_host_owns_hostsset_dead_host_ids_ensure_warmed_up)rH   r  rv  r  r  r  rs   rw  r=  r@  r_  r  s               r3   r   SandboxPool.__init__  s    l !?@@Q;455&	
"4" ,DE)A,2H	)#+,.^^%
 &NN,
 $..* #0  -,(+
 ""$ r5   r^  pool_idc                `   [        U5      nUbO  U " UR                  UR                  UR                  UR                  UUR
                  Uc  UR                  OUUSS9	$ [        US9n[        XQUS9n[        XVUS9nUR                  S5      nUR                  S5      n	U " UR                  =(       d    UR                  =(       d    [        UR                  b  [        UR                  5      OS[        UR                  S	[         5      5      U	b  [        U	5      OSUUb  [        U5      OSUUSS9	$ )
u  Reattach to a running pool by id, from any machine — no local state needed.

Finds a running host labelled with `pool_id` and rebuilds the pool's config
(image/flavor/density/host-idle) from that host job's spec and env vars, returning
a [`SandboxPool`] ready to `create()` more sandboxes — packing onto the running
hosts, or booting a duplicate (same config) when they are full.

Raises [`SandboxError`] if no running host is found (a pool stops existing once
all of its hosts are gone — idle-timed-out or killed).

Args:
    pool_id (`str`):
        The id returned when the pool was first created.
    namespace (`str`, *optional*):
        Namespace to search for the pool's hosts (defaults to yours).
    token (`str`, *optional*):
        HF token override.
NT)	r  rv  r  r  rs   rw  r=  r_  r  r}  rC  SBX_IDLE_TIMEOUTSBX_MAX_HOSTSrt  SBX_CAPACITY)r   r  rv  r  r  rw  r=  r   _find_pool_host_job	_host_envr   r   r!  r  rZ   r9   DEFAULT_SANDBOXES_PER_HOST)
r#  r  r=  r_  cacher  r  rx  idle_rawmax_hosts_raws
             r3   r-  SandboxPool.connect  s   .  (kk||#(#;#;//"//-6->%//I"
 
 % !#)DI677-.0""CcllCm&)jj&<3szz?+"377>;U#VW,9,Ec-(4*2*>XD

 
	
r5   	num_hostsc                    U R                   (       a  [        S5      eU R                  5         U R                     U[	        U R
                  5      -
  nSSS5        WS:  aA  U R                  U5      nU R                     U R
                  R                  U5        SSS5        U R                     SU l	        SSS5        U R                  5         U R                  $ ! , (       d  f       N= f! , (       d  f       NW= f! , (       d  f       NL= f)uZ  Ensure `num_hosts` empty host(s) are running and leave them running. Returns the
pool's host job ids.

Used to "create" a pool up front: the hosts carry the pool label and config (in
their env vars), so a later `SandboxPool.connect(pool_id)` (even from another
machine) finds them and spawns sandboxes without a cold start. The hosts keep
billing until killed or idle.

Adopts hosts already running for this pool (found via job labels) before booting,
so a `warm()` after `connect()` — or a repeated `warm()` — tops up to `num_hosts`
instead of duplicating live hosts and blowing past `max_hosts`.
This SandboxPool is closed.Nr   T)r  r   _discover_hostsr  rO   r  _provision_hostsextendr  r  _save_cachehost_ids)rH   r	  	shortfallbooteds       r3   warmSandboxPool.warmT  s     <<<==ZZ!C$44I q=**95F""6* "DO }} Z s#   C=C*-C;
C'*
C8;
D	)rx  rw  r{  rx  r{  re   c                   U R                   (       a  [        S5      e[        U=(       d    0 5      nU(       a  [        U R                  5      US'   Ub  [        U5      OSn/ nSnSn U R                  5          U R                  5       n	U	c)  U(       d"  SnU R                  5         U R                  5       n	U	ch  U R                  (       aF  U(       a?  U R                  (       d.  [        U R                  5        [        SU R                   S35      eU R                  U5      n	U	b>   U R                  XU5      n
U
b  U R'                  5         U
$ X;   a  UR)                  U	5        SnUS	-  nU[*        :  a  [        S
5      eGM  ! [         aF    U R                      [#        SU	R$                  S	-
  5      U	l        SSS5        e ! , (       d  f       e = ff = f! [         a    U R                      U R                   Vs/ s H  oU;  d  M
  UPM     Os  snf snU l
        SSS5        O! , (       d  f       O= fU HJ  n	 U	R-                  5         O! [         a     Of = fU	R/                  5         M7  ! U	R/                  5         f = f   e f = f)u  Create one sandbox, provisioning a host if needed.

Reuses a host with free capacity (this pool's, or a warm host found via job labels
/ the local cache) before booting a new one, so a `create()` against a warm host
costs ~one round-trip. Call it repeatedly to fan out; use `warm_up` (or [`warm`])
to pre-provision hosts and avoid a cold start on the first calls. If a host fills
up under us (another process packed it) or a cached host is gone, the sandbox is
re-placed on another host (or a fresh one).

Args:
    env (`dict[str, Any]`, *optional*):
        Environment variables for this sandbox (each sandbox gets its own).
    idle_timeout (`int` or `float` or `str`, *optional*, defaults to `600`):
        Per-sandbox idle timeout — a sandbox is evicted from its host
        after this much inactivity (no API calls, no running process). Distinct
        from the host idle timeout. Pass `None` to disable.
    forward_hf_token (`bool`, *optional*, defaults to `False`):
        If True, inject your HF token as `HF_TOKEN` in the sandbox
        (opt-in). Unlike a dedicated sandbox's `secrets`, a pooled sandbox's env is
        delivered to the host server at creation (never stored in the host job), so
        it doesn't appear in any job's metadata.
r  HF_TOKENNFr   T No running host found for pool 'X'. The pool has stopped (all its hosts were killed or idle-timed-out); create a new one.r   zZCould not place sandbox: hosts kept reporting full. Raise max_hosts or sandboxes_per_host.)r  r   r  r  r  r;   r  _reserve_oner  r  r  r   rs   _boot_one_host_create_oner   r  maxr  r  remove_MAX_PACK_ROUNDSr>  r9  )rH   rx  rw  r{  sandbox_env	idle_secs	new_hosts
discoveredroundshostr   hs               r3   r  SandboxPool.createo  sG   : <<<==39"o&6tyy&AK
# 8D7O%l3UY	*,	
<	 ""$((*<
!%J((*,,.D<..:dkk *$))4*>tyyk J_ _   ..y9D #"&"2"24i"P *((*&
 (!((."
!,,&t U 0 %  "ZZ(+Atyy1}(=DI ( (Z*  	*.++L+Q)9Kq+L !!OO%   JJLDJJL " 	s   'B;G #E? 5G 
5G ?GF=4	G=
G	GG J(H!7	HH
H!	J!
H/	+J8I	I+	
II+II+J+I==Jc                 z    U R                      [        U R                  5      sSSS5        $ ! , (       d  f       g= f)z*Number of host jobs currently provisioned.N)r  rO   r  rG   s    r3   r	  SandboxPool.num_hosts  s!     ZZt{{# ZZs   ,
:c                     U R                      [        S U R                   5       5      sSSS5        $ ! , (       d  f       g= f)z<Number of sandboxes currently handed out (across all hosts).c              3   8   #    U  H  oR                   v   M     g 7fr   )r  ).0r$  s     r3   	<genexpr>,SandboxPool.num_sandboxes.<locals>.<genexpr>  s     9[Tyy[s   N)r  sumr  rG   s    r3   num_sandboxesSandboxPool.num_sandboxes  s'     ZZ9T[[99 ZZs	   3
Ac                     U R                      U R                   Vs/ s H  oR                  PM     snsSSS5        $ s  snf ! , (       d  f       g= f)z!Job ids of the provisioned hosts.N)r  r  r  rH   r$  s     r3   r  SandboxPool.host_ids  s5     ZZ,0KK8KDKKK8 Z8 Zs   A=AA
Ac                    U R                      U R                  n/ U l        SU l        SSS5        W H5  n U R                  (       a  UR	                  5         UR                  5         M7     U R                  (       a  [        U R                  5        gg! , (       d  f       Nq= f! [
         a/  n[        R                  SUR                   SU 35         SnANSnAff = f! UR                  5         f = f)u  Release the pool. Idempotent.

For a pool we created, this terminates all host jobs (and therefore all their
sandboxes). For a `connect()`'d handle it only releases the local HTTP clients: the
shared hosts may be serving other clients, so — like [`Sandbox.connect`] — leaving a
`with` block must not tear them down. Terminate a connected pool's hosts explicitly
with `hf sandbox pool delete <id>`.
TNFailed to cancel sandbox host r  )r  r  r  r  r>  r   r  r  r  r9  r   rs   )rH   hostsr$  r   s       r3   r9  SandboxPool.close  s     ZZKKEDKDL  D##OO% 

  dii(  Z  T!?}BqcRSST 

s4   B!B$
B!$
C.%CC CC  C2c                     U $ r   rS   rG   s    r3   r  SandboxPool.__enter__   r  r5   c                 $    U R                  5         g r   )r9  r  s     r3   r  SandboxPool.__exit__  s    

r5   c                 `   U R                      U R                  (       a
   SSS5        gU R                  5         U R                  c  U R                  O[        U R                  U R                  5      nU R                     U[        U R                  5      -
  nSSS5        WS:  a  U R                  5         U R                  (       dt  U R                     U[        U R                  5      -
  nSSS5        US:  aA  U R                  U5      nU R                     U R                  R                  U5        SSS5        SU l        SSS5        U R                  (       a  U R                  5         gg! , (       d  f       N= f! , (       d  f       N= f! , (       d  f       Nb= f! , (       d  f       Nd= f)uQ  One-time: seed from the cache and pre-provision up to `warm_up` hosts.

Cheap by default: seeding is local (no HTTP), and if the cache already gives at
least `warm_up` hosts we trust them and skip discovery/booting (dead ones are
pruned lazily by `create()`). Only when short do we list_jobs and boot the
shortfall in parallel, capped by `max_hosts`.

Runs at most once per pool, under `_warmup_lock`: concurrent first `create()` calls
block here until the warm-up completes — and see its hosts — rather than racing past
a half-set flag and each booting their own. `_warmed_up` is only set once the work
succeeds, so a failed warm-up is retried (seeding/discovery dedupe by job id).

Warm-up hosts are pool-level (like [`warm`]): they are not torn down if the
triggering `create()` later fails — `close()` (or the `with` block) reclaims them.
Nr   T)r  r  _seed_hosts_from_cacher  r  r   r  rO   r  r  r  r  r  r  )rH   targetr  r  s       r3   r  SandboxPool._ensure_warmed_up  s+       '')&*nn&<T]]#dmmUYUcUcBdF"S%55	 1}$$& ..$*S-=$=	 $ 1}!%!6!6y!A!ZZ KK..v6 ("DO' , ;; #  $ (Z# sY   FAF>E,;FE=++FF2F,
E:	6F=
F	F
F	F
F-_SandboxServer | Nonec                     U R                      U R                   HB  nUR                  UR                  -
  S:  d  M"  U=R                  S-  sl        Us  sSSS5        $    SSS5        g! , (       d  f       g= f)zNReserve one slot on the first host with free capacity (under lock), else None.r   r   N)r  r  r  r  r2  s     r3   r  SandboxPool._reserve_one1  sZ    ZZ==499,q0IINIK	 Z# 
  Z
 s   +A)A)A))
A7r!  r   c                    U R                      U R                  5       nUb  UsSSS5        $ U R                  5       (       a  U R                  5       nUb  UsSSS5        $ U R                  S5      nUR	                  U5        U R
                     U R                  R	                  U5        SSS5        U R                  5       sSSS5        $ ! , (       d  f       N'= f! , (       d  f       g= f)u0  Provision (or adopt) one host and reserve a slot on it. Returns None to retry.

Held under `_boot_lock` so that, within a process, only one host is booted at a time:
a burst of create() calls that all found every host full queue here, and each new host
frees `sandboxes_per_host` slots for the threads still waiting — so they reuse it instead
of each booting their own. Before booting, we reuse a slot freed by a concurrent boot and,
failing that, adopt a host already SCHEDULING for this pool (here or in another process)
rather than piling on a duplicate.
Nr   )r  r  _adopt_pending_hostr  r  r  r  )rH   r!  r$  r  s       r3   r  SandboxPool._boot_one_host:  s     __ $$&D _ ''))((*# _ **1-FV$""6* $$&! _  _s.   C)C.CC
)C

C	C
C)c           
      L  ^^ U R                    Vs1 s H  oR                  iM     snm[        U4S jU R                  R	                  S[
        [        [        U R                  0U R                  S9 5       S5      mTc  g[        R                  SU R                   STR                   S35        [        R                  " 5       U R                  -   n[        R                  " 5       U:  a  U R                  R                  TR                  U R                  S	9R                   R"                  nUS
;  a  gUS:X  a5  U R%                  5         ['        U4S jU R                    5       5      (       a  g[        R(                  " S5        [        R                  " 5       U:  a  M  gs  snf )a  Wait for and adopt a host already SCHEDULING for this pool, if any.

Avoids over-provisioning when a host is already on its way up for this pool (started by
another process, or an earlier create() in this one): rather than booting a duplicate,
wait for it to reach RUNNING and adopt it via discovery. Returns True if a pending host
was found (whether or not it eventually came up), False if none is scheduling.
c              3   L   >#    U  H  nUR                   T;  d  M  Uv   M     g 7fr   )r  )r+  r  knowns     r3   r,  2SandboxPool._adopt_pending_host.<locals>.<genexpr>`  s,      C
 66& s   $	$
SCHEDULINGrH  r  r=  NFzPool 'z' host z: is already SCHEDULING; waiting for it instead of booting.r<  )rJ  r  r  c              3   T   >#    U  H  oR                   TR                  :H  v   M     g 7fr   r  r  )r+  r$  pendings     r3   r,  rI  w  s     I[T{{gjj0[   %(Tg      ?)r  r  nextr  	list_jobsr  r  
POOL_LABELrs   r  r  debugr  rE  r  rG  rH  rI  r  anyrL  )rH   r$  rM  rI  rH  rN  s       @@r3   rD  SandboxPool._adopt_pending_hostV  sG    *.5599..'&	:tyyI"oo /  
 ?vdii[

|;uvw99;!4!44iikH$II))t)W^^ddE55	! $$&IT[[IIIJJsO iikH$ 9 6s   F!c                 >  ^ U R                    Vs1 s H  oR                  iM     nnU R                  R                  S[        [
        [        U R                  0U R                  S9 Vs/ s H  nUR                  U;  d  M  UPM     nnU GH  mSn [        U R                  TR                  U R                  S9n[        U R                  TU R                  S9n[        UR                  SU R                  5      5      Ul        [!        UR#                  SS5      R%                  5       5      Ul        U R6                     [9        U4S
 jU R                    5       5      (       a  UR5                  5         OU R                   R;                  U5        SSS5        GM     gs  snf s  snf ! [(        [*        R,                  [.        4 aD  n[0        R3                  STR                   S	U 35        Ub  UR5                  5          SnAGMz  SnAff = f! , (       d  f       GM  = f)a%  Attach to running host jobs that match this pool (image/flavor/name).

Lets `create()` reuse a host warmed by an earlier call or another process
instead of booting a new one. Hosts are found via job labels; each adopted
host's free capacity is read from the server, so packing stays accurate.
r  rK  NrC  r  r   r  zSkipping host z during discovery: c              3   T   >#    U  H  oR                   TR                  :H  v   M     g 7fr   rM  )r+  r$  r  s     r3   r,  .SandboxPool._discover_hosts.<locals>.<genexpr>  s     E{{cff,rO  )r  r  r  rQ  r  r  rR  rs   r  r  r  r  r9   r   r  r  rO   r1  r   r  r   r  	HTTPErrorr   r  rS  r9  r  rT  r  )rH   r$  rH  r  matchesre  rx  r   s      `    r3   r  SandboxPool._discover_hosts|  s    *.55 yy** "Iz499E// + 

 vvU"  	 
 CF&tyy#&&DOOT  		3$//J"%cggnd>U>U&V"W!&.."H"M"M"OP EEEELLNKK&&v.	 !  6
& !%//>B  ~cffX5HLM%LLN s7   F(F! F!B F&=AH&H	8HH	
H	num_newc                 .   U R                      [        U R                  5      nSSS5        U R                  bK  WU-   U R                  :  a8  U R                  U-
  n[	        SU SU R                   S[        SU5       S35      e[        [        US5      S9 n[        U5       Vs/ s H  oTR                  U R                  5      PM     nnSSS5        / nSnW H#  n	 UR                  U	R                  5       5        M%     Ub,  U H$  n UR                  5         UR                  5         M&     UeU$ ! , (       d  f       GN= fs  snf ! , (       d  f       N= f! [         a  n
U
n Sn
A
M  Sn
A
ff = f! [         a     Nff = f! UR                  5         f = f)	z=Boot `num_new` host jobs in parallel, respecting `max_hosts`.NzPool needs z more host(s) but max_hosts=z allows only r   z. more. Raise max_hosts or kill some sandboxes.    )max_workers)r  rO   r  r  r   r  r   r   r   submit
_boot_hostr  r  r   r>  r9  )rH   r\  currentallowedr   _futuresr  r   futurer   re  s               r3   r  SandboxPool._provision_hosts  sj   ZZ$++&G >>%'G*;dnn*Lnnw.GgY&B4>>BR S"1g//]_   C,<=AFwPAt7GP >')"&Ffmmo. 
  #%%' LLN ! K; Z Q >=   !  LLNse   D0E$$EEEE20
D?E
E
E/"E**E/2
E?<F>E??FFc                    [        U R                  5      n[        S5      n[        X5      n[	        U R                  USSSU R
                  SUS9u  pEpgSUS'   [        U R                  5      US'   U R                  b  [        U R                  5      US'   [        S[        [        [        U R                  [        U0nU R                  R                  U R                   UUUU R"                  [$        UU=(       d    S[&        /U R(                  S	9
n	Sn
 [*        R-                  U	UUU R                  [/        U R                  S
-   S5      U R                  S9n
U
R1                  U R2                  5        U
$ ! [4         a     U R                  R7                  U	R8                  U	R:                  R                  S9  O<! [4         a/  n[<        R?                  SU	R8                   SU 35         SnAOSnAff = fU
b  U
RA                  5         e f = f)z6Start one host job and wait until its server is ready.r   NFr~  r   SBX_HOST_MODEr  r   r  r(      )r  r*   r  r  r   r  r<  r5  r  )!r  r  r   r4   r  r  rZ   r  r  r  r  r  rR  rs   r  r  r  rv  r  r  r  r   r$  r   rP  r  r   r>  r  r  r  r  r9  )rH   r)   r*   r  r  r  r  r  r  r  re  r   s               r3   ra  SandboxPool._boot_host  s   #DII."-h>5HII++"'	6
2+ $' 
 #&d&=&="> >>%'*4>>':GO$j)ZT_afgii**;;('4'(oo   
 +/	 $,,+II #D$;$;a$? E00 - F d112   	e		$$CFFciinn$M e!?xG_`a_bcdde!	s7   AE2 2
H=8F65H6
G/ %G*%H*G//Hr$  r   r\  c                    SS0nUb  X4S'   U(       a  X$S'    UR                  SSUS9R                  5       nSUl        UR                  S5      =(       d    / n[        UR                  SS5      5      (       d  U(       d'  U R                     UR                  Ul        SSS5        gUS   n[        UR                   [          US    3UUS   SSS9n	U R"                  U	l        U	$ ! [        [        R                  4 aR  nUR
                  (       a  e [        R                  S	UR                   S
U 35        U R                  U5         SnAgSnAff = f! , (       d  f       g= f)uy  Create one sandbox on a reserved `host`. Returns the sandbox, or None to retry.

None means either the host filled up between our reservation and the create
(server-authoritative capacity — another client packed it, so we mark it full and
place the sandbox elsewhere) or a host rebuilt from the cache is gone/unreachable
(dropped and re-placed via discovery / a fresh boot).
countr   Nidle_timeout_secsrx  r   r  r  z!Dropping unreachable cached host r  T	sandboxesrejectedr   r  Fr  )r1  r   r   r  rY  r  r  rS  r  
_drop_hostr   r9   r  r  r  re   r  _on_sandbox_killedrp  )
rH   r$  rx  r   bodyr   r   ro  r  r   s
             r3   r  SandboxPool._create_one  sC    !(| (1$%K	<<d<CHHJD HH[)/R	txx
A&''y MM	 |++}od4j\:$Z
  22+ eoo. 	}}LL<T[[MA3OPOOD!	 s$   C% E%E?AEE
E"r   c                     U R                      [        SUR                  R                  S-
  5      UR                  l        SSS5        g! , (       d  f       g= f)z:Free the packing slot of a shared sandbox that was killed.r   r   N)r  r  rk  r  r   s     r3   rr  SandboxPool._on_sandbox_killed+  s5    ZZ#&q'//*>*>*B#CGOO  ZZs   3A		
Ac                    U R                      U R                   Vs/ s H  o"ULd  M	  UPM     snU l        U R                  R                  UR                  5        SSS5        UR                  5         gs  snf ! , (       d  f       N$= f)zFForget a host found dead this session (and mark it for cache pruning).N)r  r  r  addr  r9  )rH   r$  r%  s      r3   rq  SandboxPool._drop_host0  s]    ZZ&*kkCkd]1kCDK##DKK0  	

 D Zs    A9A4A4-A94A99
Bc                    [        U R                  5      nUc  g[        U R                  5      nU R                     U R
                   Vs1 s H  o3R                  iM     nnUR                   H  nUR                  U;   d  UR                  UR                  :  a  M/  [        UR                  UR                  U R                  UR                  UR                  [        X%R                  5      U R                  [!        U R"                  S-   S5      UR                  S9	nUR                  Ul        SUl        U R
                  R'                  U5        M     SSS5        gs  snf ! , (       d  f       g= f)u  Adopt the pool's cached hosts without any HTTP (rebuilt from cached URL + nonce).

Best-effort and unverified: each adopted host is confirmed on the first successful
request (and dropped on the first failure, see `_create_one`). Hosts the cache
believes are full are skipped — label discovery re-checks them with fresh counts if
the seeded ones don't satisfy the request, so a stale-full entry never blocks a create.
Nr(   rj  r  F)r   rs   r  r  r  r  r  r6  r  r  r   r  r  r  r*   r4   r   r  r  r  )rH   r  r)   r$  rH  chre  s          r3   r=  "SandboxPool._seed_hosts_from_cache9  s	     		*=#DII.ZZ-1[[9[T[[[E9kk99%BKK)?'99((**[[(("7(("K		$'(?(?!(CS$I[[
 !gg"'""6*! " Z9 Zs   E"
EC5E"E""
E0c                 R   U R                      U R                   Vs/ s Ha  n[        UR                  UR                  UR
                  UR                  UR                  UR                  [        R                  " 5       S9PMc     nn[        U R                  5      nSSS5        [        U R                  U R                  U R                  U R                   U R"                  U R$                  b  ['        U R$                  5      OSU R(                  WWS9	  gs  snf ! , (       d  f       N= f)zOPersist the pool config + current hosts (with their live counts) for next time.)r  r  r  r*   r  r  
updated_atN)r  rv  r  r  rw  r=  r6  dead_host_ids)r  r  r   r  r  r  r*   r  r  rE  r  r  r   rs   r  rv  r  r  r  r;   r  )rH   r$  r6  deads       r3   r  SandboxPool._save_cacheY  s    ZZ !KK (D ;;**!]]**!]]#yy{ (   t**+D  	II**;;#66nnBFBTBTB`*4+=+=>fjoo
	
 Zs   DA(DDD
D&)r  r  r  r  r  r  r  r  r  r  r  r  r  r  rv  r  r  rs   r  )r   ro   )r+   r  )r+   r@  )*rT   rU   rV   rW   rX   r  r  r  rZ   r9   r:   r[   r   rR  r-  r   r  r  r	   r  r\   r	  r/  r  r9  r  r  r  r  r  rD  r  r   r  ra  r  re   rr  rq  r=  r  r]   rS   r5   r3   r  r    s   !J #`% ""< $1E $$ #`%`% 	`%
  `% `% :`% Dj`% EkC'$.`% :`% `% Tz`% `% 
`%H >BX\ 4
c 4
t 4
3QU: 4
an 4
 4
lc $s) < &*1E!&e #s(^d"e EkC'$.	e
 e 
eN $3 $ $
 :s : :
 9$s) 9 9
)0
'R'-=(> 'CZ '8$T $L&/P ^0D B>N >@$ 0 $tCH~ $RUX\R\ $aq $LD' Dd D
~ $ +@
r5   r  r  c                     [        U R                  [        5      (       a  U R                  O	[        5       nU(       d  [	        S5      eU$ )NzMA Hugging Face token is required to use sandboxes. Run `hf auth login` first.)r8   r_  rZ   r   r   )r  r_  s     r3   r  r  v  s3    #CIIs33CIIEjkkLr5   rx  ry  rz  rw  r{  r  c          	         0 U=(       d    0 ES[        [        5      0En0 U=(       d    0 ESU0En	[        U=(       d    / 5      n
Ub  [        [        U5      5      US'   U(       a  XS'   U R                   S[
        R                   S3US'   [        US'   U
R                  [        S	[
        R                  [        S
S95        SS[        /nXX4$ )aE  Build the (command, env, secrets, volumes) to launch a job running sbx-server.

Shared by dedicated sandboxes and shared hosts: both fetch and exec the same unified
`sbx-server` binary at startup (via `/bin/sh`), downloading it with wget/curl, or
reading it off the always-mounted server bucket when the image ships neither.
SBX_PORT	SBX_TOKENr  r  z	/buckets/z/resolve/sbx-serverSBX_SERVER_URLSBX_SERVER_MOUNTbucketT)rx   source
mount_path	read_onlyz/bin/shz-c)rZ   r  r   r;   endpointr   SANDBOX_SERVER_BUCKET_SERVER_MOUNT_PATHr  r   _BOOTSTRAP_DOWNLOAD)r  r)   rx  ry  rz  rw  r{  r  r  r  r  r  s               r3   r  r  }  s    & T#)Sj#>Q:RSG"Qgm"Qk="QKw}"%K&)*;L*I&J"#"*J#&<<.	):Y:Y9ZZm nG #5GHY%D%DQcost $ 34G[55r5   r  r=  c                    [        UR                  [        5      (       a  UR                  O0 nSU;  aF  U R                  UR                  US9R                  =(       d    0 n[        U[        5      (       a  UO0 nU$ )z6Return a host job's env vars (where pool config lives)r  r<  )r8   environmentr  rG  r  )r  r  r=  rx  s       r3   r  r    s_    '>>#//BCS ooSVVyoAMMSQST**cJr5   rC  r  c                t    U R                  S[        [        [        U0US9 H  nUs  $    [	        SU S35      e)zNReturn any running host job belonging to `pool_id` (found via the pool label).r  rK  r  r  )rQ  r  r  rR  r   )r  r  r=  r  s       r3   r  r    sT    }}*iW!MYb   
 
*7) 4K 	K r5   r  c          	         U R                  XS9nUR                  =(       d    0 nUR                  [        5      nUb  UR                  [        5      [
        :w  a  [        SU S35      eUR                  R                  S:w  a&  [        SU SUR                  R                   S35      e[        R                  UU[        [        U 5      U5      U [        R                  S-   S	9$ )
z?Reattach to a running host job and return its server transport.r<  r  z is not a sandbox host.r  zSandbox host r  rD  r  r  )rG  r  r   r  r  r  r   rH  rI  r   r$  r4   r  r~   r   )r  r  r=  r  r  r*   s         r3   r  r    s    
///
BCZZ2FJJ{#E}

:.);T+.EFGG
zz9$];-7PQTQ[Q[QaQaPbbdeff""+,<S,A5I$99A= #  r5   r`  c                     SU ;  a  X4$ U R                  SS5      u  p#U(       a  U(       a  SU;   a  [        SU < S35      eUb  X:w  a  [        SU< SU< S35      eX24$ )zPAccept `namespace/sandbox_id` ids (as shown in the Hub UI), like `hf jobs` does.r  r   z?Sandbox id must be 'sandbox_id' or 'namespace/sandbox_id', got r#   z%Conflicting namespace: got namespace=z" but sandbox id implies namespace=)r  r   )r`  r=  extracted_namespace	parsed_ids       r3   r  r    s    
*$$%/%5%5c1%="i3)3C\]g\jjklmm!A3I=@bcvbyyz{
 	
 ))r5   c                     U R                   R                  =(       d    /  H  nS[         S3U;   d  M  Us  $    [        SU R                   S[         S35      e)Nz--r#   r  z) does not expose the sandbox server port )rH  expose_urlsr  r   r  )r  urls     r3   r"  r"    sZ    zz%%++#$A&#-J , cffX%NObNccde
ffr5      )r=  limitr  r  c                     [        U R                  XS95      U* S  nU(       a  SSR                  S U 5       5      -   $ S$ ! [         a     gf = f)Nr<  r{   z Last logs:

c              3   ,   #    U  H
  nS U 3v   M     g7f)z  NrS   )r+  r   s     r3   r,  !_tail_job_logs.<locals>.<genexpr>  s     &EutD6{us   )r   fetch_job_logsr   r   )r  r  r=  r  liness        r3   rK  rK    sb    S''v'KLeVWU JO?TYY&Eu&EEEVTVV  s   A 
AA)^r0   r-   r   r  rE  concurrent.futuresr   
contextlibr   dataclassesr   r   pathlibr   ry  r   typingr	   r
   r   r   r   r   r   r  r{   r   _sandbox_cacher   r   r   r   
_space_apir   errorsr   r   r   hf_apir   r   utilsr   r   utils._parsingr   
get_loggerrT   r  r  r  r  r  r  rR  r  r  r  r  r  r  rJ  r  r  r  rZ   r4   r9   r:   r;   r=   r_   rq   r~   r[   r  r   rS  r   r   r   rU  re   r  r  r   r   r  r  r  r  r  r"  rK  rS   r5   r3   <module>r     s        1 % (   M M M     E E " % * 
		H	%   
	 
 !    A    ' 	 cC c c c$ec 1 $c $ s s s& C C C.   AN ANH	sT#Y 	td{ 	tCH~ 	5>> htn s s4 sxk xkv "{ {|^
 ^
B% C #6	#6#6 
c3h$		#6
 #s(^d"#6 &\D #6 +#d*#6 #6 #6 49d38nd38nd6lBC#6L5 w cDj T#s(^  NR 	U 	S 	d
 	V] 	 LP u 3 cDj Tb $*# *#* *sCRVJAW *g' gc g HLZ\ W Ws W#* WTW Wad Wr5   