U
    ”Ê¹j@<  ã                   @   s¶  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	m
Z
 d dlmZ d dlmZmZmZmZmZmZ ddlmZmZmZ e e¡Zdd	d
dhZddddhZe	G dd„ dƒƒZeejef eeef dœdd„Z eeejf eeeeeee!f f  ddœdd„Z"eeejf eeejf ddœdd„Z#ej$eeeeje!f ddœdd„Z%eeee!f e!dœdd „Z&eed!œd"d#„Z'eee dd$œd%d&„Z(ej$ej)e*d'œd(d)„Z+dS )*é    N)Úcontextmanager)Ú	dataclassÚfield)ÚPath)ÚAnyÚDictÚ	GeneratorÚIterableÚTupleÚUnioné   )ÚDDUFCorruptedFileErrorÚDDUFExportErrorÚDDUFInvalidEntryNameErrorz.jsonz.modelz.safetensorsz.txtzconfig.jsonztokenizer_config.jsonzpreprocessor_config.jsonzscheduler_config.jsonc                   @   sj   e Zd ZU dZeed< eed< eed< edd�Ze	ed< e
eeddf d	œd
d„ƒZdeedœdd„ZdS )Ú	DDUFEntrya¿  Object representing a file entry in a DDUF file.

    See [`read_dduf_file`] for how to read a DDUF file.

    Attributes:
        filename (str):
            The name of the file in the DDUF archive.
        offset (int):
            The offset of the file in the DDUF archive.
        length (int):
            The length of the file in the DDUF archive.
        dduf_path (str):
            The path to the DDUF archive (for internal use).
    ÚfilenameÚlengthÚoffsetF)ÚreprÚ	dduf_pathN©Úreturnc              
   c   sV   | j  d¡�@}tj| ¡ dtjd��}|| j| j| j … V  W 5 Q R X W 5 Q R X dS )a-  Open the file as a memory-mapped file.

        Useful to load safetensors directly from the file.

        Example:
            ```py
            >>> import safetensors.torch
            >>> with entry.as_mmap() as mm:
            ...     tensors = safetensors.torch.load(mm)
            ```
        Úrbr   )r   ÚaccessN)r   ÚopenÚmmapÚfilenoÚACCESS_READr   r   )ÚselfÚfÚmm© r!   úb/var/www/html/bwave/python/venv/lib/python3.8/site-packages/huggingface_hub/serialization/_dduf.pyÚas_mmap9   s    zDDUFEntry.as_mmapúutf-8)Úencodingr   c              
   C   sD   | j  d¡�.}| | j¡ | | j¡j|d�W  5 Q R £ S Q R X dS )zÕRead the file as text.

        Useful for '.txt' and '.json' entries.

        Example:
            ```py
            >>> import json
            >>> index = json.loads(entry.read_text())
            ```
        r   )r%   N)r   r   Úseekr   Úreadr   Údecode)r   r%   r   r!   r!   r"   Ú	read_textJ   s    zDDUFEntry.read_text)r$   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__ÚstrÚ__annotations__Úintr   r   r   r   r   Úbytesr#   r)   r!   r!   r!   r"   r   "   s   
r   )r   r   c                 C   s$  i }t | ƒ} t d| › �¡ t t| ƒd¡�¢}| ¡ D ]’}t d|j› �¡ |j	tj
kr`tdƒ‚zt|jƒ W n4 tk
r¢ } ztd|j› �ƒ|‚W 5 d}~X Y nX t||ƒ}t|j||j| d�||j< q6W 5 Q R X d|krätd	ƒ‚t |d  ¡ ¡}t|| ¡ ƒ t d
| › dt|ƒ› d�¡ |S )a  
    Read a DDUF file and return a dictionary of entries.

    Only the metadata is read, the data is not loaded in memory.

    Args:
        dduf_path (`str` or `os.PathLike`):
            The path to the DDUF file to read.

    Returns:
        `Dict[str, DDUFEntry]`:
            A dictionary of [`DDUFEntry`] indexed by filename.

    Raises:
        - [`DDUFCorruptedFileError`]: If the DDUF file is corrupted (i.e. doesn't follow the DDUF format).

    Example:
        ```python
        >>> import json
        >>> import safetensors.torch
        >>> from huggingface_hub import read_dduf_file

        # Read DDUF metadata
        >>> dduf_entries = read_dduf_file("FLUX.1-dev.dduf")

        # Returns a mapping filename <> DDUFEntry
        >>> dduf_entries["model_index.json"]
        DDUFEntry(filename='model_index.json', offset=66, length=587)

        # Load model index as JSON
        >>> json.loads(dduf_entries["model_index.json"].read_text())
        {'_class_name': 'FluxPipeline', '_diffusers_version': '0.32.0.dev0', '_name_or_path': 'black-forest-labs/FLUX.1-dev', ...

        # Load VAE weights using safetensors
        >>> with dduf_entries["vae/diffusion_pytorch_model.safetensors"].as_mmap() as mm:
        ...     state_dict = safetensors.torch.load(mm)
        ```
    zReading DDUF file ÚrzReading entry z)Data must not be compressed in DDUF file.z!Invalid entry name in DDUF file: N)r   r   r   r   úmodel_index.jsonú7Missing required 'model_index.json' entry in DDUF file.zDone reading DDUF file z. Found z entries)r   ÚloggerÚinfoÚzipfileÚZipFiler.   ÚinfolistÚdebugr   Úcompress_typeÚ
ZIP_STOREDr   Ú_validate_dduf_entry_namer   Ú_get_data_offsetr   Ú	file_sizeÚjsonÚloadsr)   Ú_validate_dduf_structureÚkeysÚlen)r   ÚentriesÚzfr6   Úer   Úindexr!   r!   r"   Úread_dduf_fileZ   s2    '$
   ÿrI   )r   rE   r   c                 C   sz  t  d| › d�¡ tƒ }d}t t| ƒdtj¡�à}|D ]Ô\}}||krTtd|› �ƒ‚| |¡ |dkr¬zt	 
t|ƒ ¡ ¡}W n. t	jk
rª } ztdƒ|‚W 5 d}~X Y nX zt|ƒ}W n2 tk
rê } ztd|› �ƒ|‚W 5 d}~X Y nX t  d	|› d
�¡ t|||ƒ q6W 5 Q R X |dk�r(tdƒ‚zt||ƒ W n. tk
�rd } ztdƒ|‚W 5 d}~X Y nX t  d| › �¡ dS )a¯  Write a DDUF file from an iterable of entries.

    This is a lower-level helper than [`export_folder_as_dduf`] that allows more flexibility when serializing data.
    In particular, you don't need to save the data on disk before exporting it in the DDUF file.

    Args:
        dduf_path (`str` or `os.PathLike`):
            The path to the DDUF file to write.
        entries (`Iterable[Tuple[str, Union[str, Path, bytes]]]`):
            An iterable of entries to write in the DDUF file. Each entry is a tuple with the filename and the content.
            The filename should be the path to the file in the DDUF archive.
            The content can be a string or a pathlib.Path representing a path to a file on the local disk or directly the content as bytes.

    Raises:
        - [`DDUFExportError`]: If anything goes wrong during the export (e.g. invalid entry name, missing 'model_index.json', etc.).

    Example:
        ```python
        # Export specific files from the local disk.
        >>> from huggingface_hub import export_entries_as_dduf
        >>> export_entries_as_dduf(
        ...     dduf_path="stable-diffusion-v1-4-FP16.dduf",
        ...     entries=[ # List entries to add to the DDUF file (here, only FP16 weights)
        ...         ("model_index.json", "path/to/model_index.json"),
        ...         ("vae/config.json", "path/to/vae/config.json"),
        ...         ("vae/diffusion_pytorch_model.fp16.safetensors", "path/to/vae/diffusion_pytorch_model.fp16.safetensors"),
        ...         ("text_encoder/config.json", "path/to/text_encoder/config.json"),
        ...         ("text_encoder/model.fp16.safetensors", "path/to/text_encoder/model.fp16.safetensors"),
        ...         # ... add more entries here
        ...     ]
        ... )
        ```

        ```python
        # Export state_dicts one by one from a loaded pipeline
        >>> from diffusers import DiffusionPipeline
        >>> from typing import Generator, Tuple
        >>> import safetensors.torch
        >>> from huggingface_hub import export_entries_as_dduf
        >>> pipe = DiffusionPipeline.from_pretrained("CompVis/stable-diffusion-v1-4")
        ... # ... do some work with the pipeline

        >>> def as_entries(pipe: DiffusionPipeline) -> Generator[Tuple[str, bytes], None, None]:
        ...     # Build an generator that yields the entries to add to the DDUF file.
        ...     # The first element of the tuple is the filename in the DDUF archive (must use UNIX separator!). The second element is the content of the file.
        ...     # Entries will be evaluated lazily when the DDUF file is created (only 1 entry is loaded in memory at a time)
        ...     yield "vae/config.json", pipe.vae.to_json_string().encode()
        ...     yield "vae/diffusion_pytorch_model.safetensors", safetensors.torch.save(pipe.vae.state_dict())
        ...     yield "text_encoder/config.json", pipe.text_encoder.config.to_json_string().encode()
        ...     yield "text_encoder/model.safetensors", safetensors.torch.save(pipe.text_encoder.state_dict())
        ...     # ... add more entries here

        >>> export_entries_as_dduf(dduf_path="stable-diffusion-v1-4.dduf", entries=as_entries(pipe))
        ```
    zExporting DDUF file 'ú'NÚwzCan't add duplicate entry: r3   z#Failed to parse 'model_index.json'.zInvalid entry name: zAdding entry 'z' to DDUF filer4   zInvalid DDUF file structure.zDone writing DDUF file )r5   r6   Úsetr7   r8   r.   r<   r   Úaddr@   rA   Ú_load_contentr(   ÚJSONDecodeErrorr=   r   r:   Ú_dump_content_in_archiverB   r   )r   rE   Ú	filenamesrH   Úarchiver   ÚcontentrG   r!   r!   r"   Úexport_entries_as_ddufŸ   s4    :
"
rT   )r   Úfolder_pathr   c                    s6   t ˆ ƒ‰ tttt f  dœ‡ fdd„}t| |ƒ ƒ dS )a  
    Export a folder as a DDUF file.

    AUses [`export_entries_as_dduf`] under the hood.

    Args:
        dduf_path (`str` or `os.PathLike`):
            The path to the DDUF file to write.
        folder_path (`str` or `os.PathLike`):
            The path to the folder containing the diffusion model.

    Example:
        ```python
        >>> from huggingface_hub import export_folder_as_dduf
        >>> export_folder_as_dduf(dduf_path="FLUX.1-dev.dduf", folder_path="path/to/FLUX.1-dev")
        ```
    r   c                  3   sz   t ˆ ƒ d¡D ]f} |  ¡ sq| jtkr:t d| › d�¡ q|  ˆ ¡}t|j	ƒdkrft d| › d�¡ q| 
¡ | fV  qd S )Nz**/*zSkipping file 'z' (file type not allowed)é   z"' (nested directories not allowed))r   ÚglobÚis_fileÚsuffixÚDDUF_ALLOWED_ENTRIESr5   r:   Úrelative_torD   ÚpartsÚas_posix)ÚpathZpath_in_archive©rU   r!   r"   Ú_iterate_over_folder  s    

z3export_folder_as_dduf.<locals>._iterate_over_folderN)r   r	   r
   r.   rT   )r   rU   r`   r!   r_   r"   Úexport_folder_as_ddufú   s    ra   )rR   r   rS   r   c              
   C   s‚   | j |ddd��h}t|ttfƒrNt|ƒ}|  d¡�}t ||d¡ W 5 Q R X n&t|tƒrd| |¡ ntd|› d�ƒ‚W 5 Q R X d S )NrK   T)Úforce_zip64r   i  € zInvalid content type for z. Must be str, Path or bytes.)	r   Ú
isinstancer.   r   ÚshutilÚcopyfileobjr1   Úwriter   )rR   r   rS   Z
archive_fhZcontent_pathZ
content_fhr!   r!   r"   rP     s    
rP   )rS   r   c                 C   s@   t | ttfƒrt| ƒ ¡ S t | tƒr(| S tdt| ƒ› d�ƒ‚dS )zoLoad the content of an entry as bytes.

    Used only for small checks (not to dump content into archive).
    z6Invalid content type. Must be str, Path or bytes. Got Ú.N)rc   r.   r   Ú
read_bytesr1   r   Útype)rS   r!   r!   r"   rN   *  s
    
rN   )Ú
entry_namer   c                 C   sh   d|   d¡d  tkr$td| › �ƒ‚d| kr<td| › d�ƒ‚|  d¡} |  d¡dkrdtd| › d�ƒ‚| S )	Nrg   éÿÿÿÿzFile type not allowed: ú\z0Entry names must use UNIX separators ('/'). Got ú/é   z-DDUF only supports 1 level of directory. Got )ÚsplitrZ   r   ÚstripÚcount)rj   r!   r!   r"   r=   7  s    
r=   )rH   Úentry_namesr   c                    s€   t | tƒstdt| ƒ› d�ƒ‚dd„ ˆ D ƒ}|D ]J‰ˆ| krLtdˆ› d�ƒ‚t‡ ‡fdd„tD ƒƒs0td	ˆ› d
t› d�ƒ‚q0dS )a�  
    Consistency checks on the DDUF file structure.

    Rules:
    - The 'model_index.json' entry is required and must contain a dictionary.
    - Each folder name must correspond to an entry in 'model_index.json'.
    - Each folder must contain at least a config file ('config.json', 'tokenizer_config.json', 'preprocessor_config.json', 'scheduler_config.json').

    Args:
        index (Any):
            The content of the 'model_index.json' entry.
        entry_names (Iterable[str]):
            The list of entry names in the DDUF file.

    Raises:
        - [`DDUFCorruptedFileError`]: If the DDUF file is corrupted (i.e. doesn't follow the DDUF format).
    z>Invalid 'model_index.json' content. Must be a dictionary. Got rg   c                 S   s"   h | ]}d |kr|  d ¡d ’qS )rm   r   )ro   )Ú.0Úentryr!   r!   r"   Ú	<setcomp>W  s      z+_validate_dduf_structure.<locals>.<setcomp>zMissing required entry 'z' in 'model_index.json'.c                 3   s    | ]}ˆ› d |› �ˆ kV  qdS )rm   Nr!   )rs   Zrequired_entry©rr   Úfolderr!   r"   Ú	<genexpr>[  s     z+_validate_dduf_structure.<locals>.<genexpr>z!Missing required file in folder 'z!'. Must contains at least one of N)rc   Údictr   ri   ÚanyÚDDUF_FOLDER_REQUIRED_ENTRIES)rH   rr   Zdduf_foldersr!   rv   r"   rB   B  s    
ÿrB   )rF   r6   r   c                 C   s€   | j dkrtdƒ‚|j}| j  |¡ | j  d¡}t|ƒdk rDtdƒ‚t |dd… d¡}t |dd… d¡}|d | | }|S )a1  
    Calculate the data offset for a file in a ZIP archive.

    Args:
        zf (`zipfile.ZipFile`):
            The opened ZIP file. Must be opened in read mode.
        info (`zipfile.ZipInfo`):
            The file info.

    Returns:
        int: The offset of the file data in the ZIP archive.
    Nz+ZipFile object must be opened in read mode.é   zIncomplete local file header.é   é   Úlittle)Úfpr   Úheader_offsetr&   r'   rD   r0   Ú
from_bytes)rF   r6   r�   Zlocal_file_headerZfilename_lenZextra_field_lenZdata_offsetr!   r!   r"   r>   a  s    
r>   ),r@   Úloggingr   Úosrd   r7   Ú
contextlibr   Údataclassesr   r   Úpathlibr   Útypingr   r   r   r	   r
   r   Úerrorsr   r   r   Ú	getLoggerr*   r5   rZ   r{   r   ÚPathLiker.   rI   r1   rT   ra   r8   rP   rN   r=   rB   ÚZipInfor0   r>   r!   r!   r!   r"   Ú<module>   sD    
û
û	7"F þ[&$"