U
    Cvf                     @  s*  d dl mZ d dlZd dl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mZmZmZ d dlmZ d dlmZ d dlmZ d dlmZmZmZmZ d d	lmZ d
Zdd ZG dd deZ dd Z!dd Z"d%ddZ#d&ddZ$dd Z%dd Z&G dd deZ'd'd d!Z(G d"d# d#eZ)e)ed$< dS )(    )annotationsN)codingconventions)BACKEND_ENTRYPOINTSAbstractWritableDataStoreBackendArrayBackendEntrypoint_encode_variable_name_normalize_path)StoreBackendEntrypoint)indexing)integer_types)
FrozenDictHiddenKeyDictclose_on_errormodule_available)VariableZ_ARRAY_DIMENSIONSc                 C  s4   t | tjr|  }nt | tjr,|  }n| }|S )a  
    Encode a attribute value as something that can be serialized as json

    Many xarray datasets / variables have numpy arrays and values. This
    function handles encoding / decoding of such items.

    ndarray -> list
    scalar array -> scalar
    other -> other (no change)
    )
isinstancenpZndarraytolistZgenericitem)valueencoded r   8/tmp/pip-unpacked-wheel-h316xyqg/xarray/backends/zarr.pyencode_zarr_attr_value!   s    

r   c                   @  s(   e Zd ZdZdd Zdd Zdd ZdS )	ZarrArrayWrapper)	datastoredtypeshapevariable_namec                 C  s,   || _ || _|  }|j| _|j}|| _d S N)r   r    	get_arrayr   r   )selfr    r   arrayr   r   r   r   __init__9   s    zZarrArrayWrapper.__init__c                 C  s   | j j| j S r!   )r   
zarr_groupr    r#   r   r   r   r"   C   s    zZarrArrayWrapper.get_arrayc                 C  s`   |   }t|tjr||j S t|tjr@|jt|| jj S t|tj	sPt
|j|j S d S r!   )r"   r   r   ZBasicIndexertupleZVectorizedIndexerZvindexZ_arrayize_vectorized_indexerr   ZOuterIndexerAssertionErrorZoindex)r#   keyr$   r   r   r   __getitem__F   s    
zZarrArrayWrapper.__getitem__N)__name__
__module____qualname__	__slots__r%   r"   r+   r   r   r   r   r   6   s   
r   c              	   C  sZ  |s| sdS |rv| svt dd |D r<td|d|dt dd |D rdtd|d	|d
tdd |D S t| tr|| f }nt| }t||krtd||||S |D ]$}t|tstd|d|dq|s|S |rN|rNt	||D ]P\}}|dd D ]:}	|	| rd|d|d|d}
|rt
|
d qq|S tddS )zd
    Given encoding chunks (possibly None or []) and variable chunks
    (possibly None or []).
    Nc                 s  s&   | ]}t t|d d dkV  qd S )N   )lenset.0chunksr   r   r   	<genexpr>k   s     z)_determine_zarr_chunks.<locals>.<genexpr>zIZarr requires uniform chunk sizes except for final chunk. Variable named z has incompatible dask chunks: z&. Consider rechunking using `chunk()`.c                 s  s   | ]}|d  |d k V  qdS )r   r0   Nr   r4   r   r   r   r7   q   s     zZFinal chunk of Zarr array must be the same size or smaller than the first. Variable named z has incompatible Dask chunks zb.Consider either rechunking using `chunk()` or instead deleting or modifying `encoding['chunks']`.c                 s  s   | ]}|d  V  qdS )r   Nr   )r5   chunkr   r   r   r7   y   s     zwzarr chunk sizes specified in `encoding['chunks']` must be an int or a tuple of ints. Instead found encoding['chunks']=z for variable named .r0   z)Specified zarr chunks encoding['chunks']=z$ would overlap multiple dask chunks zH. Writing this array in parallel with dask could lead to corrupted data.zx Consider either rechunking using `chunk()`, deleting or modifying `encoding['chunks']`, or specify `safe_chunks=False`.z7We should never get here. Function logic must be wrong.)any
ValueErrorr(   r   r   r2   _determine_zarr_chunksint	TypeErrorzipNotImplementedErrorr)   )Z
enc_chunksZ
var_chunksndimnamesafe_chunksZenc_chunks_tuplexZzchunkZdchunksZdchunkZ
base_errorr   r   r   r<   U   sL    

	

r<   c           	      C  s   z| j | }W n tk
r } z|s6td| d|tj| jd}t| j| }zdd |d d D }W n4 tk
r } ztd| d|W 5 d }~X Y nX W 5 d }~X Y nX d	d | j D }t| j |g| }||fS )
Nz&Zarr object is missing the attribute `zA`, which is required for xarray to determine variable dimensions.z.zarrayc                 S  s   g | ]}t j|qS r   )ospathbasenamer5   dimr   r   r   
<listcomp>   s    z,_get_zarr_dims_and_attrs.<locals>.<listcomp>Z_NCZARR_ARRAYZdimrefszZ` and the NCZarr metadata, which are required for xarray to determine variable dimensions.c                 S  s   g | ]}| d r|qS Z_NC
startswith)r5   attrr   r   r   rJ      s     
 )	attrsKeyErrorrE   rF   joinjsonloadsstorer   )	zarr_objZdimension_key
try_nczarr
dimensionseZzarray_pathZzarrayZnc_attrs
attributesr   r   r   _get_zarr_dims_and_attrs   s0    



&rZ   FTc                   s   | j  }dddddh |rB fdd|D }|r^td|nt|D ]}| krJ||= qJt|d| j| j||}||d< |S )	z
    Extract zarr encoding dictionary from xarray Variable

    Parameters
    ----------
    variable : Variable
    raise_on_invalid : bool, optional

    Returns
    -------
    encoding : dict
        Zarr encoding for `variable`
    r6   
compressorfiltersZcache_metadataZwrite_empty_chunksc                   s   g | ]}| kr|qS r   r   )r5   kZvalid_encodingsr   r   rJ      s      z2extract_zarr_variable_encoding.<locals>.<listcomp>z2unexpected encoding parameters for zarr backend:  )encodingcopyr;   listr<   getr6   rA   )variableraise_on_invalidrB   rC   r_   invalidr]   r6   r   r^   r   extract_zarr_variable_encoding   s2    
    rf   c                 C  s:   t j| |d} tjjdd}|j| |d} tj| } | S )a  
    Converts an Variable into an Variable which follows some
    of the CF conventions:

        - Nans are masked using _FillValue (or the deprecated missing_value)
        - Rescaling via: scale_factor and add_offset
        - datetimes are converted to the CF 'units since time' format
        - dtype encodings are enforced.

    Parameters
    ----------
    var : Variable
        A variable holding un-encoded data.

    Returns
    -------
    out : Variable
        A variable which has been encoded as described above.
    )rB   T)Zallows_unicode)r   Zencode_cf_variabler   stringsZEncodedStringCoderencodeZensure_fixed_length_bytes)varZ
needs_copyrB   Zcoderr   r   r   encode_zarr_variable  s
    rj   c              
     s   |j |j kr,td| d|j  d|j  di }|j D ]P\}}|d k	rz||krz|| |\}}	}
|
dksrt|	| }| kr:|||< q: fdd|j D }||krtd| d| d| d	 d
	d S )Nz	variable z/ already exists with different dimension names  != zA, but changing variable dimensions is not supported by to_zarr().r1   c                   s   i | ]\}}| kr||qS r   r   )r5   rI   size
append_dimr   r   
<dictcomp>8  s       z+_validate_existing_dims.<locals>.<dictcomp>z0 already exists with different dimension sizes: z]. to_zarr() only supports changing dimension sizes when explicitly appending, but append_dim=r9   )dimsr;   Zsizesitemsindicesr)   )var_namenew_varexisting_varregionrn   Zexisting_sizesrI   rl   startstopZstrideZ	new_sizesr   rm   r   _validate_existing_dims&  s"    
ry   c              
   C  sB   z| j | W n, tk
r< } ztd|W 5 d}~X Y nX | S )z9Raise a more informative error message for invalid attrs.z#Invalid attribute in Dataset.attrs.N)rO   putr>   )rU   rO   rX   r   r   r   
_put_attrsB  s
    r{   c                   @  s   e Zd ZdZdZed&dd	Zd'd
dZedd Z	dd Z
dd Zdd Zdd Zd(ddZdd Zdd Zdd Ze ddfddZd d! Zd)d"d#Zd$d% ZdS )*	ZarrStorez+Store for reading and writing data via zarr)	r&   _append_dim_consolidate_on_close_group_mode
_read_only_synchronizer_write_region_safe_chunksrNFT   c                 C  sT  dd l }t|tjrt|}|d kr2t|dd}t|||d}||d< |dkr||d< |s`|rvtd| d| d	|d krd
}|r||d< |d krd
}|d krz|j|f|}W nd t	k
r   z"|j
|f|}tjdt|d W n* |jjk
r   td| dY nX Y nX n$|r4|j|f|}n|j
|f|}| ||||	|
|S )Nr   Z_store_versionr   )modesynchronizerrF   storage_optionszarr_versionz@consolidated metadata has not been implemented for zarr version z. yet. Set consolidated=False for zarr version zC. See also https://github.com/zarr-developers/zarr-specs/issues/136Fchunk_storea  Failed to open Zarr store with consolidated metadata, but successfully read with non-consolidated metadata. This is typically much slower for opening a dataset. To silence this warning, consider:
1. Consolidating metadata in this existing store with zarr.consolidate_metadata().
2. Explicitly setting consolidated=False, to avoid trying to read consolidate metadata, or
3. Explicitly setting consolidated=True, to raise an error in this case instead of falling back to try reading non-consolidated metadata.)
stacklevelzNo such file or directory: '')zarrr   rE   PathLikefspathgetattrdictr;   Zopen_consolidatedrP   
open_groupwarningswarnRuntimeWarningerrorsZGroupNotFoundErrorFileNotFoundError)clsrT   r   r   groupconsolidatedconsolidate_on_closer   r   rn   write_regionrC   r   r   r   Zopen_kwargsr&   r   r   r   r   Z  s^    


zZarrStore.open_groupc                 C  sF   || _ | j j| _| j j| _| j j| _|| _|| _|| _	|| _
|| _d S r!   )r&   Z	read_onlyr   r   r   rF   r   r   r~   r}   r   r   )r#   r&   r   r   rn   r   rC   r   r   r   r%     s    	


zZarrStore.__init__c                 C  s   | j S r!   )r&   r'   r   r   r   ds  s    zZarrStore.dsc                 C  s   t t|| }| jdk}t|t|\}}t|}|dd  |jtt	||j|j
|jd}t|dd k	rv|j|d< t||||S )Nr   r\   )r6   Zpreferred_chunksr[   r\   
fill_value
_FillValue)r   ZLazilyIndexedArrayr   r   rZ   DIMENSION_KEYr   popr6   r?   r[   r\   r   r   r   )r#   rB   
zarr_arraydatarV   rW   rY   r_   r   r   r   open_store_variable  s"    
  
zZarrStore.open_store_variablec                   s   t  fdd j D S )Nc                 3  s"   | ]\}}|  ||fV  qd S r!   )r   r5   r]   vr'   r   r   r7     s    z*ZarrStore.get_variables.<locals>.<genexpr>)r   r&   arraysr'   r   r'   r   get_variables  s    zZarrStore.get_variablesc                 C  s   dd | j j  D S )Nc                 S  s    i | ]\}}| d s||qS rK   rL   r   r   r   r   ro     s   
 z'ZarrStore.get_attrs.<locals>.<dictcomp>)r&   rO   Zasdictrq   r'   r   r   r   	get_attrs  s    zZarrStore.get_attrsc           	   
   C  s   | j dk}i }| j D ]j\}}t|t|\}}t||jD ]D\}}||krx|| |krxtd| d| d||  d|||< q<q|S )Nr   z(found conflicting lengths for dimension z (rk   ))r   r&   r   rZ   r   r?   r   r;   )	r#   rV   rW   r]   r   Z	dim_names_dsr   r   r   get_dimensions  s    
zZarrStore.get_dimensionsc                 C  s   |d k	rt dd S )Nz<Zarr backend doesn't know how to handle unlimited dimensions)r@   )r#   	variablesunlimited_dimsr   r   r   set_dimensions  s    zZarrStore.set_dimensionsc                 C  s   t | j| d S r!   )r{   r&   )r#   rY   r   r   r   set_attributes  s    zZarrStore.set_attributesc                 C  s   t |}|S r!   )rj   )r#   rc   r   r   r   encode_variable  s    zZarrStore.encode_variablec                 C  s   t |S r!   )r   )r#   ar   r   r   encode_attribute  s    zZarrStore.encode_attributec                   s0  ddl } fddD }t| }fdd|D }	 |	|\}
}|rt    \}}}i }|D ](}| jdd||< || j|| _qn |i \}}|
	| |D ](}|
| }|| }t
||| j j q jd	kr |  j|
|d
  j|
|||d
  jr,| jj dS )a(  
        Top level method for putting data on this store, this method:
          - encodes variables/attributes
          - sets dimensions
          - sets variables

        Parameters
        ----------
        variables : dict-like
            Dictionary of key/value (variable name / xr.Variable) pairs
        attributes : dict-like
            Dictionary of key/value (attribute name / attribute) pairs
        check_encoding_set : list-like
            List of variables that should be checked for invalid encoding
            values
        writer : ArrayWriter
        unlimited_dims : list-like
            List of dimension names that should be treated as unlimited
            dimensions.
            dimension on which the zarray will be appended
            only needed in append mode
        r   Nc                   s   h | ]}t | jkr|qS r   )r	   r&   r5   vnr'   r   r   	<setcomp>.  s     z"ZarrStore.store.<locals>.<setcomp>c                   s   i | ]}| | qS r   r   r   )r   r   r   ro   2  s      z#ZarrStore.store.<locals>.<dictcomp>F)deep)r   zr+)r   )r   r3   rh   r   Zdecode_cf_variablesr   r   r`   r_   updatery   r   r}   r   r   r   set_variablesr~   Zconsolidate_metadatar&   rT   )r#   r   rY   check_encoding_setwriterr   r   Zexisting_variable_namesZnew_variablesZvariables_without_encodingZvariables_encodedZexisting_varsr   Zvars_with_encodingr   rs   rt   ru   r   )r#   r   r   rT     sT    
  


   zZarrStore.storec                 C  s   d S r!   r   r'   r   r   r   sync[  s    zZarrStore.syncc                   s  |  D ]\}}t|}||k}|j }	|j}
|j}|j}|	dd}|jddikrf|dkrfi |_|| j	kr|| j	| }nxt
|||| jd}i }|
|t< |	  D ]\}}| |||< qtj|tkrt}| j	j|f|||d|}t||}| jdk	r| jni   fdd|
D  | jdk	r| j|
kr|
| j} | j tdksXtt|j| d | j< t|j}||  |j| 7  < || t fdd|
D }||j|| qdS )	a  
        This provides a centralized method to set the variables on the data
        store.

        Parameters
        ----------
        variables : dict-like
            Dictionary of key/value (variable name / xr.Variable) pairs
        check_encoding_set : list-like
            List of variables that should be checked for invalid encoding
            values
        writer
        unlimited_dims : list-like
            List of dimension names that should be treated as unlimited
            dimensions.
        r   N)rd   rB   rC   )r   r   r   c                   s   i | ]}|  |td qS r!   )rb   slicerH   r   r   r   ro     s      z+ZarrStore.set_variables.<locals>.<dictcomp>c                 3  s   | ]} | V  qd S r!   r   rH   r   r   r   r7     s     z*ZarrStore.set_variables.<locals>.<genexpr>)rq   r	   rO   r`   rp   r   r   r   r_   r&   rf   r   r   r   r   rg   Zcheck_vlen_dtypestrcreater{   r   r}   indexr   r)   ra   resizer(   addr   )r#   r   r   r   r   r   r   rB   checkrO   rp   r   r   r   r   r_   Zencoded_attrsZk2Zv2Zappend_axisZ	new_shaperv   r   r   r   r   ^  s`    

     
 


zZarrStore.set_variablesc                 C  s   d S r!   r   r'   r   r   r   close  s    zZarrStore.close)r   NNFFNNNNTr   N)NFNNT)N)N)r,   r-   r.   __doc__r/   classmethodr   r%   propertyr   r   r   r   r   r   r   r   r   	frozensetrT   r   r   r   r   r   r   r   r|   K  sN               [     



M
Hr|   autoc                 K  s   ddl m} |dkr>zddl}i }W n tk
r<   d}Y nX |rXtdd|  ||
|||d|d}|| ||||||d	||	||||d
}|S )a  Load and decode a dataset from a Zarr store.

    The `store` object should be a valid store for a Zarr group. `store`
    variables must contain dimension metadata encoded in the
    `_ARRAY_DIMENSIONS` attribute or must have NCZarr format.

    Parameters
    ----------
    store : MutableMapping or str
        A MutableMapping where a Zarr Group has been stored or a path to a
        directory in file system where a Zarr DirectoryStore has been stored.
    synchronizer : object, optional
        Array synchronizer provided to zarr
    group : str, optional
        Group path. (a.k.a. `path` in zarr terminology.)
    chunks : int or dict or tuple or {None, 'auto'}, optional
        Chunk sizes along each dimension, e.g., ``5`` or
        ``{'x': 5, 'y': 5}``. If `chunks='auto'`, dask chunks are created
        based on the variable's zarr chunks. If `chunks=None`, zarr array
        data will lazily convert to numpy arrays upon access. This accepts
        all the chunk specifications as Dask does.
    overwrite_encoded_chunks : bool, optional
        Whether to drop the zarr chunks encoded for each variable when a
        dataset is loaded with specified chunk sizes (default: False)
    decode_cf : bool, optional
        Whether to decode these variables, assuming they were saved according
        to CF conventions.
    mask_and_scale : bool, optional
        If True, replace array values equal to `_FillValue` with NA and scale
        values according to the formula `original_values * scale_factor +
        add_offset`, where `_FillValue`, `scale_factor` and `add_offset` are
        taken from variable attributes (if they exist).  If the `_FillValue` or
        `missing_value` attribute contains multiple values a warning will be
        issued and all array values matching one of the multiple values will
        be replaced by NA.
    decode_times : bool, optional
        If True, decode times encoded in the standard NetCDF datetime format
        into datetime objects. Otherwise, leave them encoded as numbers.
    concat_characters : bool, optional
        If True, concatenate along the last dimension of character arrays to
        form string arrays. Dimensions will only be concatenated over (and
        removed) if they have no corresponding variable and if they are only
        used as the last dimension of character arrays.
    decode_coords : bool, optional
        If True, decode the 'coordinates' attribute to identify coordinates in
        the resulting dataset.
    drop_variables : str or iterable, optional
        A variable or list of variables to exclude from being parsed from the
        dataset. This may be useful to drop variables with problems or
        inconsistent values.
    consolidated : bool, optional
        Whether to open the store using zarr's consolidated metadata
        capability. Only works for stores that have already been consolidated.
        By default (`consolidate=None`), attempts to read consolidated metadata,
        falling back to read non-consolidated metadata if that fails.

        When the experimental ``zarr_version=3``, ``consolidated`` must be
        either be ``None`` or ``False``.
    chunk_store : MutableMapping, optional
        A separate Zarr store only for chunk data.
    storage_options : dict, optional
        Any additional parameters for the storage backend (ignored for local
        paths).
    decode_timedelta : bool, optional
        If True, decode variables and coordinates with time units in
        {'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds'}
        into timedelta objects. If False, leave them encoded as numbers.
        If None (default), assume the same value of decode_time.
    use_cftime : bool, optional
        Only relevant if encoded dates come from a standard calendar
        (e.g. "gregorian", "proleptic_gregorian", "standard", or not
        specified).  If None (default), attempt to decode times to
        ``np.datetime64[ns]`` objects; if this is not possible, decode times to
        ``cftime.datetime`` objects. If True, always decode times to
        ``cftime.datetime`` objects, regardless of whether or not they can be
        represented using ``np.datetime64[ns]`` objects.  If False, always
        decode times to ``np.datetime64[ns]`` objects; if this is not possible
        raise an error.
    zarr_version : int or None, optional
        The desired zarr spec version to target (currently 2 or 3). The default
        of None will attempt to determine the zarr version from ``store`` when
        possible, otherwise defaulting to 2.

    Returns
    -------
    dataset : Dataset
        The newly created dataset.

    See Also
    --------
    open_dataset
    open_mfdataset

    References
    ----------
    http://zarr.readthedocs.io/
    r   )open_datasetr   Nz-open_zarr() got unexpected keyword arguments ,   )r   r   overwrite_encoded_chunksr   r   r   r   r   )filename_or_objr   	decode_cfmask_and_scaledecode_timesconcat_charactersdecode_coordsZenginer6   drop_variablesbackend_kwargsdecode_timedelta
use_cftimer   )Zxarray.backends.apir   Z
dask.arrayImportErrorr>   rQ   keys)rT   r   r   r6   r   r   r   r   r   r   r   r   r   r   r   r   r   kwargsr   Zdaskr   r   r   r   r   	open_zarr  sH    u

r   c                   @  s2   e Zd ZdZedZdZdZdd ZdddZ	dS )ZarrBackendEntrypointz
    Backend for ".zarr" files based on the zarr package.

    For more information about the underlying library, visit:
    https://zarr.readthedocs.io/en/stable

    See Also
    --------
    backends.ZarrStore
    r   z,Open zarr files (.zarr) using zarr in XarrayzVhttps://docs.xarray.dev/en/stable/generated/xarray.backends.ZarrBackendEntrypoint.htmlc                 C  s4   zt j|\}}W n tk
r*   Y dS X |dkS )NF>   .zarr)rE   rF   splitextr>   )r#   r   r   extr   r   r   guess_can_open[  s
    z$ZarrBackendEntrypoint.guess_can_openTNr      c                 C  sb   t |}tj||	|
||d|||d |d
}t }t|  |j||||||||d}W 5 Q R X |S )NFr1   )	r   r   r   r   r   r   r   r   r   )r   r   r   r   r   r   r   )r
   r|   r   r   r   r   )r#   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   rT   Zstore_entrypointr   r   r   r   r   b  s4    

z"ZarrBackendEntrypoint.open_dataset)TTTTNNNNr   NNNNr   N)
r,   r-   r.   r   r   	availabledescriptionurlr   r   r   r   r   r   r   K  s*   
               r   r   )FNT)TN)NNr   TTTTTNNFNNNNN)*
__future__r   rR   rE   r   Znumpyr   Zxarrayr   r   Zxarray.backends.commonr   r   r   r   r	   r
   Zxarray.backends.storer   Zxarray.corer   Zxarray.core.pycompatr   Zxarray.core.utilsr   r   r   r   Zxarray.core.variabler   r   r   r   r<   rZ   rf   rj   ry   r{   r|   r   r   r   r   r   r   <module>   s\    `#     
.
!	  c                
 "H