o
    2jc                  
   @   s   U d Z ddlZddlZddlmZmZ ddlmZmZm	Z	 ddl
mZ ddlmZmZ ddlmZ d	d
 ej D Zeeef ed< edZeej Zee ed< eh dZee ed< eddhZee ed< eddG dd dZeddG dd dZdede fddZ!ej"dedefddZ#dede fdd Z$d!edefd"d#Z%d$edefd%d&Z&d'edefd(d)Z'd*ed+ede(eedB e dB f fd,d-Z)d.ed+ede(ej*ef fd/d0Z+d.ed1ej*d+edefd2d3Z,d.ed1ej*d+edefd4d5Z-dS )6a  Centralized parser for Hugging Face Hub URIs ('hf://...') and mount specifications.

A HF URI is a URI-like string that identifies a location on the Hugging Face
Hub: a model/dataset/space/kernel repository, a bucket, optionally a revision,
and optionally a path inside the repo or bucket.

Canonical syntax:

```
hf://[<TYPE>/]<ID>[@<REVISION>][/<PATH>]
```

For convenience, [`parse_hf_uri`] also accepts Hugging Face **web URLs** (the
ones you copy-paste from your browser), e.g.
'https://huggingface.co/datasets/my-org/my-dataset/blob/main/train.csv'. They are
normalized to the canonical 'hf://' form before parsing. Only unambiguous URLs
(repository / bucket pages and file/folder viewer routes) are accepted; any other
route is rejected rather than guessed.

A HF mount wraps a HF URI with a local mount path and an optional ':ro'/':rw'
flag (used by Spaces and Jobs volumes):

```
hf://[<TYPE>/]<ID>[@<REVISION>][/<PATH>]:<MOUNT_PATH>[:ro|:rw]
```

See 'docs/source/en/package_reference/hf_uris.md' for the full grammar and examples.
    N)	dataclassfield)quoteunquoteurlsplit)	constants)
HfUriErrorHFValidationError   )validate_repo_idc                 C   s   i | ]\}}||qS  r   ).0kvr   r   Y/var/www/html/whisper/venv/lib/python3.10/site-packages/huggingface_hub/utils/_hf_uris.py
<dictcomp>8   s    r   _TYPE_TO_PREFIXz ^refs/(?:convert/[\w.-]+|pr/\d+)_VALID_URI_TYPES>   rawblobtreeblameresolve_URL_REPO_LOCATION_ACTIONSr   r   _URL_BUCKET_LOCATION_ACTIONST)frozenc                   @   s   e Zd ZU dZejed< eed< dZedB ed< dZ	eed< e
ddddd	ZedB ed
< dddZedefddZedefddZdefddZddedB defddZdS )HfUria  Parsed representation of a Hugging Face Hub URI ('hf://...').

    Attributes:
        type (`str`):
            One of 'model', 'dataset', 'space', 'kernel' or 'bucket'.
        id (`str`):
            The repository id ('namespace/name', e.g. 'my-org/my-model') for repo URIs, or the bucket id ('namespace/name') for bucket URIs.
        revision (`str`, *optional*):
            The revision specified after '@' in the URI, URL-decoded. 'None' if no revision was specified, or for bucket URIs (which
            never carry a revision). Special refs like 'refs/pr/10' and 'refs/convert/parquet' are preserved as-is.
        path_in_repo (`str`):
            The path inside the repo or bucket. Empty string if the URI points at the root.
    typeidNrevision path_in_repoFreprhashcomparedefault_rawreturnc              
   C   s  | j pd}| jtvrt|d| j dtt dd| jr%| jddkr0t|d| j d	d| jd
krRzt| j W n tyQ } z	t|t	|d|d }~ww | j
d ur`| j
s`t|dd| jd
krp| j
d urpt|dd| jr| jds~d| jv rt|d| j ddd S d S )Nr    zInvalid type 'z'. Must be one of .urimsg/r
   z"Id must be 'namespace/name', got ''.bucketz%Revision must not be an empty string.z&Bucket URIs do not support a revision.//z+Path must not contain empty segments (got 'z').)r'   r   r   r   sortedr   countr   r	   strr   r!   
startswith)selfr+   er   r   r   __post_init__d   s*   

 
zHfUri.__post_init__c                 C   s
   | j dkS )z$True if this URI points at a bucket.r/   r   r5   r   r   r   	is_bucket      
zHfUri.is_bucketc                 C   s
   | j dkS )zJTrue if this URI points at a repository (model, dataset, space or kernel).r/   r8   r9   r   r   r   is_repo   r;   zHfUri.is_repoc                 C   sz   t jt| j d| jg}| jdur,| j}d|v r$t|du r$|dd}|	d|  | j
r8|	d| j
  d|S )zRender the URI as a canonical 'hf://' string.

        The type prefix is always written explicitly (e.g. 'hf://models/my-org/my-model').
        r-   N%2F@r    )r   HF_PROTOCOLr   r   r   r   _SPECIAL_REFS_REVISION_REGEX	fullmatchreplaceappendr!   join)r5   partsr   r   r   r   to_uri   s   

zHfUri.to_uriendpointc                 C   s   |pt jd}t| jdd}| jdkr'| d| j }|r%|d| 7 }|S | jdkr4| d| j n| dt| j  d| j }| j}|durZd|v rZt	
|du rZ|dd}|rk|d	|pbt j d| 7 }|S |durv|d| 7 }|S )
a  Render the URI as a Hugging Face **web URL** (the kind you open in a browser).

        This is the inverse of parsing a URL with [`parse_hf_uri`]. The returned URL points at:

        - the repository / bucket landing page when no path or revision is set;
        - the folder viewer ('/tree/<revision>') when only a revision is set;
        - the file viewer ('/blob/<revision>/<path>') for repository files (revision defaults to 'main');
        - the tree route ('/tree/<path>') for bucket files (buckets are not versioned).

        Args:
            endpoint (`str`, *optional*):
                Base endpoint to use. Defaults to 'constants.ENDPOINT' (i.e. 'https://huggingface.co').

        Returns:
            `str`: the web URL.

        Example:
            ```py
            >>> from huggingface_hub import parse_hf_uri
            >>> parse_hf_uri("hf://datasets/my-org/my-dataset@v1/train.csv").to_url()
            'https://huggingface.co/datasets/my-org/my-dataset/blob/v1/train.csv'
            ```
        r-   )safer/   z	/buckets/z/tree/modelNr=   z/blob/)r   ENDPOINTrstripr   r!   r   r   r   r   r@   rA   rB   DEFAULT_REVISION)r5   rG   basepathurlr   r   r   r   to_url   s"   
6zHfUri.to_urlr(   NN)__name__
__module____qualname____doc__r   	HfUriType__annotations__r3   r   r!   r   r'   r7   propertyboolr:   r<   rF   rP   r   r   r   r   r   N   s   
 

r   c                   @   sf   e Zd ZU dZeed< eed< dZedB ed< e	dddddZ
edB ed< dd
dZd	efddZdS )HfMountaF  A HF URI paired with a local mount path and optional read-only flag.

    Used by Spaces and Jobs to describe volume mounts. The full syntax is:

    ```
    hf://[<TYPE>/]<ID>[@<REVISION>][/<PATH>]:<MOUNT_PATH>[:ro|:rw]
    ```

    Attributes:
        source ([`HfUri`]):
            The parsed HF URI identifying the Hub resource to mount.
        mount_path (`str`):
            The local mount path (always starts with '/').
        read_only (`bool`, *optional*):
            True if the mount ends with ':ro', False if it ends with ':rw', 'None' if no flag was provided.
    source
mount_pathN	read_onlyFr"   r'   r(   c                 C   s:   | j pd}| jdr| jdkrt|d| j ddd S )Nr    r-   zEMount path must be a non-empty absolute path starting with '/', got 'r.   r*   )r'   r]   r4   r   )r5   r   r   r   r   r7      s   
zHfMount.__post_init__c                 C   s:   | j  d| jg}| jdur|| jrdnd d|S )zqRender the mount as a canonical 'hf://' string.

        Example: 'hf://models/my-org/my-model:/data:ro'
        :N:ro:rwr    )r\   rF   r]   r^   rC   rD   )r5   rE   r   r   r   rF      s   

zHfMount.to_urirQ   )rS   rT   rU   rV   r   rX   r3   r^   rZ   r   r'   r7   rF   r   r   r   r   r[      s   
 
r[   r+   r(   c                 C   s$   zt |  W dS  ty   Y dS w )zVCheck if a string is a valid HF URI ('hf://...') or a recognized Hugging Face web URL.TF)parse_hf_urir   r+   r   r   r   	is_hf_uri   s   rd   c                 C   s   | }|  tjr| ttjd }|st| dtj dnt| r't| }nt| dtj dtj dt||d\}}|dkrHt|||dS t	|||dS )	al  Parse a Hugging Face Hub URI ('hf://...') or a Hugging Face web URL.

    A HF URI is a URI-like string identifying a location on the Hugging Face Hub. The full grammar is:

    ```
    hf://[<TYPE>/]<ID>[@<REVISION>][/<PATH>]
    ```

    For convenience, Hugging Face **web URLs** (the ones you copy-paste from the website) are also
    accepted and normalized to the canonical 'hf://' form, e.g.
    'https://huggingface.co/datasets/my-org/my-dataset/blob/main/train.csv'. Only unambiguous URLs
    (repository / bucket pages and file/folder viewer routes) are accepted; any other route is rejected.

    See 'docs/source/en/package_reference/hf_uris.md' for the full specification.

    Args:
        uri (`str`):
            The URI to parse. Must start with 'hf://', or be a Hugging Face URL (e.g. 'https://huggingface.co/...').

    Returns:
        [`HfUri`]: the parsed URI.

    Raises:
        [`HfUriError`]:
            If the URI is malformed (missing prefix, invalid type, missing id, unsupported URL route, etc.).

    Examples:
        ```py
        >>> from huggingface_hub.utils import parse_hf_uri
        >>> parse_hf_uri("hf://my-org/my-model")
        HfUri(type='model', id='my-org/my-model', revision=None, path_in_repo='')
        >>> parse_hf_uri("hf://datasets/my-org/my-dataset@refs/pr/3/train.json")
        HfUri(type='dataset', id='my-org/my-dataset', revision='refs/pr/3', path_in_repo='train.json')
        >>> parse_hf_uri("https://huggingface.co/datasets/my-org/my-dataset/blob/main/train.csv")
        HfUri(type='dataset', id='my-org/my-dataset', revision='main', path_in_repo='train.csv')
        ```
    NEmpty body after 'r.   Must start with 'zQ' or be a Hugging Face URL (e.g. 'https://huggingface.co/...'). Expected format: z#[<TYPE>/]<ID>[@<REVISION>][/<PATH>]r   r/   )
r4   r   r?   lenr   _looks_like_hf_url_url_to_uri_body_split_type_parse_bucket_body_parse_repo_body)r+   r   bodytype_locationr   r   r   rb      s$   '

rb   c                    s.   |     drdS t fddtjD S )zNReturn True if 'uri' looks like a (possibly scheme-less) Hugging Face web URL.)zhttp://zhttps://Tc                 3   s&    | ]} |kp  |d  V  qdS )r-   N)r4   )r   hostloweredr   r   	<genexpr>B  s   $ z%_looks_like_hf_url.<locals>.<genexpr>)lowerr4   anyr   HF_URL_HOSTSrc   r   rr   r   ri   <  s   
ri   segmentc                 C   s   t | ddS )aw  Percent-decode a single URL path segment (e.g. 'file%20name.txt' -> 'file name.txt').

    A decoded '/' is re-encoded as '%2F' so the segment stays atomic when the normalized body is
    re-split by the shared parser. This decodes ordinary path characters (spaces, '#', ...) that
    browsers encode, while keeping '%2F'-encoded revisions (e.g. 'feature%2Ffoo') intact.
    r-   r=   )r   rB   )rx   r   r   r   _decode_url_path_segmentE  s   ry   rO   c                 C   s  | }t d| v r	| nd|  }|jpd }|tjvr&t|d|p |  dddd |jd	D }|s=t|d
|  ddd}|d tjv rP|d }|dd }t	|dk r`t|d|  dd|d  d	|d  }|dd }|dkr|s|d| S |^}}	|t
vrt|d|  d| ddd	dd |	D }
|
rd| d	|
 S d| S |r| d	nd}|s| | S |^}}	|tvrt|d|  d| dd|	s| | S d	dd |	D }| | d| S )an  Normalize a Hugging Face web URL into the body of a 'hf://' URI (everything after 'hf://').

    The returned string is fed back into the regular URI parsing logic, so all validation
    (repo id, revision, empty path segments, ...) is shared with the canonical 'hf://' path.
    Only unambiguous URLs are accepted: any unrecognized route raises [`HfUriError`].
    z://r0   r    zUnrecognized host 'zC'. Expected a Hugging Face URL (e.g. 'https://huggingface.co/...').r*   c                 S   s   g | ]}|r|qS r   r   r   rx   r   r   r   
<listcomp>a  s    z$_url_to_uri_body.<locals>.<listcomp>r-   z0Missing repository or bucket identifier in URL 'r.   Nr   r
      zCannot parse URL 'z{': expected a '<namespace>/<name>' repository or bucket. User/organization pages and single-segment URLs are not supported.bucketszbuckets/zCannot parse bucket URL 'z': unsupported '/z	/' route.c                 s       | ]}t |V  qd S rR   ry   rz   r   r   r   rt   ~      z#_url_to_uri_body.<locals>.<genexpr>zl/' route. Only repository pages and file/folder viewer routes (blob, resolve, raw, tree, ...) can be parsed.c                 s   r~   rR   r   rz   r   r   r   rt     r   r>   )r   hostnameru   r   rw   r   rN   splitHF_URI_TYPE_PREFIXESrh   r   rD   r   )rO   r   parsedrq   segmentstype_prefixrepo_idrestactiontailrN   prefixdecodedr   r   r   rj   O  sV   


rj   	mount_strc           	   
   C   s   |  tjst| dtj dd| }| ttjd }|s)t|dtj ddt||d\}}}|du r<t|ddtj| }zt|}W n ty[ } zt||jd|d}~ww t||||dS )	a  Parse a HF mount specification ('hf://...:<MOUNT_PATH>[:ro|:rw]').

    A mount specification is a HF URI followed by a local mount path and an optional read-only/read-write flag.
    The full grammar is:

    ```
    hf://[<TYPE>/]<ID>[@<REVISION>][/<PATH>]:<MOUNT_PATH>[:ro|:rw]
    ```

    See 'docs/source/en/package_reference/hf_uris.md' for the full specification.

    Args:
        mount_str (`str`):
            The mount string to parse. Must start with 'hf://' and contain a ':<MOUNT_PATH>' segment.

    Returns:
        [`HfMount`]: the parsed mount.

    Raises:
        [`HfUriError`]:
            If the mount string is malformed (missing mount path, invalid URI, etc.).

    Examples:
        ```py
        >>> from huggingface_hub.utils import parse_hf_mount
        >>> parse_hf_mount("hf://my-org/my-model:/data:ro")
        HfMount(source=HfUri(type='model', id='my-org/my-model', revision=None, path_in_repo=''), mount_path='/data', read_only=True)
        >>> parse_hf_mount("hf://buckets/my-org/my-bucket/sub/dir:/mnt:rw")
        HfMount(source=HfUri(type='bucket', id='my-org/my-bucket', revision=None, path_in_repo='sub/dir'), mount_path='/mnt', read_only=False)
        ```
    rf   r.   r*   Nre   rg   zKMissing mount path. Expected ':<MOUNT_PATH>' (e.g. 'hf://org/model:/data').)r\   r]   r^   r'   )	r4   r   r?   r   rh   _split_mountrb   r,   r[   )	r   r   rn   rp   r]   r^   uri_strr\   r6   r   r   r   parse_hf_mount  s(    
r   rn   r   c                C   s   |  drd| d}} n|  drd| d}} nd}| d}|dkr6|dur1t|dd	| ddfS | d| }| |d
 d }|sLt|dd	|||fS )zSplit the ':<MOUNT_PATH>[:ro|:rw]' suffix from 'body'.

    Returns '(location, mount_path, read_only)' where 'mount_path' is 'None' if no mount segment is present.
    r`   Tra   FNz:/zb':ro'/':rw' suffix is only valid when a mount path is provided (e.g. 'hf://...:/<MOUNT_PATH>:ro').r*   r
   z#Missing location before mount path.)endswithremovesuffixrfindr   )rn   r   r^   idxrp   r]   r   r   r   r     s$   




r   rp   c                C   s   |  d}|dkr8| tjv rt|d|  dtj |  ddt|  }dur4t|dtj | d	dd
| fS | d| }| |d d }|tjv rRtj| |fS t| }durht|d| d| ddd
| fS )zDetect the (optional) type prefix and return '(type, remaining_location)'.

    A missing type prefix defaults to 'model'. Singular forms ('model/', 'dataset/', etc.) are explicitly rejected with a helpful error.
    r-   r   zMissing identifier after 'z'. Expected 'z/<ID>'.r*   Nz*Type prefix must be plural. Did you mean 'z/...'?rI   r
   z!Type prefix must be plural, got 'z/'. Did you mean 'z/'?)findr   r   r   r?   r   get)rp   r   	slash_idxsingular_pluralfirstr   r   r   r   rk     s,   


rk   ro   c                C   s   |  d} | dd}t|dk s|d r|d s#t|d|  dd|d  d|d  }d|v r8t|d	dt|d
krB|d nd}t||d||dS )z8Parse the body of a bucket URI: 'namespace/name[/path]'.r-   r|   r   r
   z)Bucket id must be 'namespace/name', got 'r.   r*   r>   z3Bucket URIs do not support a revision marker ('@').   r    Nr   r   r   r!   r'   )stripr   rh   r   r   )rp   ro   r   rE   	bucket_idpath_in_bucketr   r   r   rl     s   
rl   c                C   s  |  d} | st|dd| d}|dks!| d| ddkrQd}| dd}t|dk r9t|d	|  d
d|d  d|d  }t|dkrN|d nd}nh| d| }| |d d }|sgt|dd|ddkrxt|d	| ddt|}	|	dur|	 }|t|d 	d}n|d}
|
dkr|}d}n|d|
 }||
d d }t
|}|st|ddt|||||dS )z@Parse the body of a repo URI: '<repo_id>[@<revision>][/<path>]'.r-   zMissing repository id.r*   r>   r   Nr
   r|   z-Repository id must be 'namespace/name', got 'z'. r   r    z!Missing repository id before '@'.r.   zEmpty revision after '@'.r   )r   r   r   r2   r   rh   r@   matchgroupremoveprefixr   r   )rp   ro   r   at_idxr   rE   r   r!   rev_and_pathr   r   r   r   r   rm   '  sJ   



rm   ).rV   	functoolsredataclassesr   r   urllib.parser   r   r   huggingface_hubr   huggingface_hub.errorsr   r	   _validatorsr   r   itemsr   dictr3   rX   compiler@   	frozensetvaluesr   r   r   r   r[   rZ   rd   	lru_cacherb   ri   ry   rj   r   tupler   rW   rk   rl   rm   r   r   r   r   <module>   sX   $
}*	;	
I(: 
