
    `gj#                        d Z ddlmZ ddlZddlZddlZddlZddlZddlZddl	m
Z
 ddlmZ ddlmZ  ej                  e      ZddZddZ G d	 d
e      ZddZd ZddZddZddZddZdddZy)u  Managed uv — one path, no guessing.

Hermes owns its own uv binary at ``$HERMES_HOME/bin/uv`` (or ``uv.exe`` on
Windows).  Every code path that needs uv resolves it from that single location.
If the binary is missing, ``ensure_uv()`` bootstraps it via the official
standalone installer with ``UV_UNMANAGED_INSTALL`` / ``UV_INSTALL_DIR`` pointed
at ``$HERMES_HOME/bin`` so the installer writes directly there — no PATH
probing, no conda guards, no multi-location resolution chains.
    )annotationsN)Path)Optional)get_hermes_homec                 d    t               } t        j                         dk(  r| dz  dz  S | dz  dz  S )u   Return the path where Hermes keeps *its* uv binary.

    ``$HERMES_HOME/bin/uv`` on POSIX, ``$HERMES_HOME\bin\uv.exe`` on
    Windows.  The directory may not exist yet — callers should use
    ``ensure_uv()`` to bootstrap it.
    Windowsbinzuv.exeuv)r   platformsystem)homes    H/root/.hermes/venv/lib/python3.12/site-packages/hermes_cli/managed_uv.pymanaged_uv_pathr      s9     DI%e|h&&%<$    c                     t               } | j                         r/t        j                  | t        j                        rt        |       S y)ub   Return the managed uv path if it exists, else ``None``.

    No side effects — pure lookup.
    N)r   is_fileosaccessX_OKstr)ps    r   
resolve_uvr   +   s2    
 	Ayy{ryyBGG,1vr   c                  8     e Zd ZU dZded<   dd fdZd Z xZS )	_UvResultu  ``ensure_uv()`` return value that survives an update boundary.

    ``ensure_uv()``'s arity has flipped between a single path string and a
    ``(path, fresh_bootstrap)`` tuple across releases. ``hermes update`` runs
    the call site from the *old*, already-imported ``hermes_cli.main`` against
    this *freshly pulled* module, so the two can disagree on how many values
    ``ensure_uv()`` returns. An install parked on a 2-tuple release runs
    ``uv_bin, fresh_bootstrap = ensure_uv()`` against the single-value module
    and crashes the first update: the returned path is a plain ``str``, which is
    itself iterable, so the 2-target unpack walks its characters and raises
    ``ValueError: too many values to unpack (expected 2)`` (and on the failure
    path the ``None`` return raises ``TypeError: cannot unpack non-iterable
    NoneType``). This wrapper answers to both conventions:

        uv_bin = ensure_uv()         # behaves as the path str ("" when absent)
        uv_bin, fresh = ensure_uv()  # unpacks as (path|None, fresh_bootstrap)

    Missing uv is the empty string (falsy) instead of ``None`` so legacy
    2-target call sites can still unpack a failure without raising, while
    ``if not uv_bin`` keeps working for single-value callers.

    POSIX only. This wrapper is **never** returned on Windows — see
    ``ensure_uv()`` for why the ``__iter__`` override is unsafe there.
    boolfresh_bootstrapc                >    t         |   | |xs d      }||_        |S )N )super__new__r   )clspathfreshself	__class__s       r   r    z_UvResult.__new__R   s$    wsDJB/$r   c                J    t        t        |       xs d | j                  f      S N)iterr   r   )r$   s    r   __iter__z_UvResult.__iter__W   s#     c$i'4$*>*>?@@r   )F)r"   Optional[str]r#   r   returnz'_UvResult')__name__
__module____qualname____doc____annotations__r    r)   __classcell__)r%   s   @r   r   r   6   s    2 
Ar   r   c                    t               } | r| S t               }|j                  j                  dd       t	        d|j                   d       	 t        |       t               }|rDt        j                  |dgddd	
      j                  j                         }t	        d| d       |S t	        d       |S # t        $ r.}t        j                  d|       t	        d|        Y d}~yd}~ww xY w)zQResolve the managed uv path, installing it if necessary (plain ``str``/``None``).T)parentsexist_oku!     → Installing managed uv into z ...zManaged uv install failed: %su$     ✗ Failed to install managed uv: N	--versionFcapture_outputtextchecku     ✓ Managed uv installed ()uA     ✗ Managed uv install appeared to succeed but binary not found)r   r   parentmkdirprint_install_uv	Exceptionloggerwarning
subprocessrunstdoutstrip)existingtargetexcresultversions        r   _ensure_uv_pathrK   ^   s    |HF
MMt4	-fmm_D
ABF \F..[!	

 & 	 	,WIQ78 M 	QRM#  6<4SE:;s   B8 8	C/$C**C/c                 ^    t               } t        j                         dk(  r| S t        |       S )u  Return the managed uv path, installing it first if necessary.

    On **POSIX** the result is a :class:`_UvResult` (a ``str`` subclass) that is
    both usable directly as the path *and* unpackable as
    ``(path, fresh_bootstrap)`` for older call sites parked on a 2-tuple
    release — see :class:`_UvResult` for the update-boundary rationale.

    On **Windows** we deliberately return a plain ``str``/``None`` instead.
    ``subprocess`` there serializes the argv via ``subprocess.list2cmdline``,
    which iterates every entry *as a string* (``for c in arg``). The dependency
    installer passes uv straight into the command list (``[uv_bin, "pip", ...]``),
    so a ``_UvResult`` — whose ``__iter__`` yields ``(path, fresh_bootstrap)``
    rather than characters — would inject the bool into the command line and
    crash the install with ``TypeError: sequence item 1: expected str instance,
    bool found``. A plain ``str`` matches the historical Windows contract and is
    subprocess-safe. (A single value cannot satisfy both 2-target unpacking and
    Windows char-iteration: both use the iterator protocol, with contradictory
    results.)

    On failure the result is falsy — never raises — so callers can fall back to
    pip gracefully.
    r   )rK   r   r   r   )rI   s    r   	ensure_uvrM      s-    . FI% Vr   c                 T   t               } | syt        j                  | ddgddd      }|j                  dk(  rDt        j                  | dgddd      j                  j                         }t        d	| d
       | S t        j                  d|j                  |j                         | S )u  Run ``uv self update`` on the managed uv binary.

    Call this during ``hermes update`` so the managed copy stays current.
    Returns the managed path on success, ``None`` if uv isn't available or
    the self-update fails (non-fatal — the old version still works).
    Nr$   updateTFr6   r   r5   u     ✓ Managed uv updated (r:   z!uv self update failed (rc=%d): %s)
r   rB   rC   
returncoderD   rE   r=   r@   debugstderr)rF   rI   rJ   s      r   update_managed_uvrS      s     |H^^	68$	F A..{#	

 & 	 	*7)156 O 	8&:K:KV]][Or   c                    t        j                         }i t        j                  t	        | j
                        t	        | j
                        d}|dk(  rt        |       yt        |       y)a  Bootstrap uv into *target* using the official standalone installer.

    Uses ``UV_UNMANAGED_INSTALL`` (POSIX) or ``UV_INSTALL_DIR`` (Windows)
    so the astral installer writes the binary directly into
    ``$HERMES_HOME/bin/`` instead of ``~/.local/bin/``.
    )UV_UNMANAGED_INSTALLUV_INSTALL_DIRr   N)r   r   r   environr   r;   _install_uv_windows_install_uv_posix)rG   r   envs      r   r>   r>      s\     __F
**
 !$FMM 2fmm,C C #r   c                   t        j                  dd      5 }|j                  }ddd       	 t        j                  ddddgd	d	
       t        j                  d|g| d	d	       	 t        j                  |       y# 1 sw Y   YxY w# t        $ r Y yw xY w# 	 t        j                         w # t        $ r Y w w xY wxY w)zHDownload + sh the POSIX installer (two-stage to avoid curl|sh pitfalls).z.shF)suffixdeleteNcurlz-LsSfzhttps://astral.sh/uv/install.shz-oT)r9   r7   shrZ   r9   r7   )tempfileNamedTemporaryFilenamerB   rC   r   unlinkOSError)rZ   finstaller_paths      r   rY   rY      s    		$	$E%	@  A W?~V	

 	>"		
	IIn%#   $  			IIn% 		sL   A=8B 'B	 =B		BBB?B0/B?0	B<9B?;B<<B?c                D    d}t        j                  dddd|g| dd       y)	z Invoke the PowerShell installer.z*irm https://astral.sh/uv/install.ps1 | iex
powershellz-ExecutionPolicyBypassz-cTr`   N)rB   rC   )rZ   cmds     r   rX   rX      s2     	5  NN	)8T3?	r   c                     y r'    )uv_binvenv_dirpython_versions      r   rebuild_venvrq      s    r   )r+   r   )r+   r*   )rG   r   r+   None)rZ   zdict[str, str]r+   rr   )z3.11)rn   r   ro   r   rp   r   r+   r   )r/   
__future__r   loggingr   r   shutilrB   ra   pathlibr   typingr   hermes_constantsr   	getLoggerr,   r@   r   r   r   r   rK   rM   rS   r>   rY   rX   rq   rm   r   r   <module>rz      sz    #  	       ,			8	$
%A %APB>H.0
	r   