B
    lc0                 @  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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mZ d dlmZ d dlZd dlZd dlmZ d dlmZ d dlZejdkrd dlZd	d
 Zdd Z n0ejdkrd dl!Z!dd
 Zdd Z ndd
 ZeZ ej"dd Z#ddddddZ$dddddZ%d/ddddddd d!d"Z&d#d$d%d&d'Z'dd(d)d*d+d,Z(G d-d. d.Z)dS )0    )annotationsN)Path)AnyOptionalUnion)urlparse)HTTPAdapter)Retryposixc             C  s   t | t j d S )N)fcntllockfZLOCK_EX)file_handle r   2lib/python3.7/site-packages/dials_data/download.py_platform_lock   s    r   c             C  s   t | t j d S )N)r   r   ZLOCK_UN)r   r   r   r   _platform_unlock   s    r   ntc          
   C  s`   |  d xPyt|  tjd P W q tk
rV } z|jtjkrF W d d }~X Y qX qW d S )Nr      )seekmsvcrtlockingfilenoZLK_LOCKOSErrorerrnoZEDEADLK)r   er   r   r   r   #   s    
c             C  s"   |  d t|  tjd d S )Nr   r   )r   r   r   r   ZLK_UNLCK)r   r   r   r   r   /   s    
c             C  s   t dd S )Nz+File locking not supported on this platform)NotImplementedError)r   r   r   r   r   5   s    c             c  s.   d}zt |  d}dV  W d|r(t|  X dS )z
    Cross-platform file locking. Open a file for writing or appending.
    Then a file lock can be obtained with:

    with open(filename, 'w') as fh:
      with _file_lock(fh):
        (..)
    FTN)r   r   )r   lockr   r   r   
_file_lock;   s    

r   zrequests.Sessionstrr   )sessionurlpyfilec          
   C  sn   | j |ddV}|  |jjddd |jdd&}x|jddD ]}|| qDW W dQ R X W dQ R X dS )	z+
    Downloads a single URL to a file.
    T)stream)parentsexist_okwb)modei   )Z
chunk_sizeN)getZraise_for_statusparentmkdiropenZiter_contentwrite)r   r    r!   rfchunkr   r   r   _download_to_fileO   s    r/   )file_to_hashreturnc          	     sL   t  }| d, x$t fdddD ]}|| q(W W dQ R X | S )z$Returns the SHA256 digest of a file.rbc                 s
     dS )Ni   )readr   )r-   r   r   <lambda>_       zfile_hash.<locals>.<lambda>r5   N)hashlibZsha256r*   iterupdateZ	hexdigest)r0   Zsha256_hashblockr   )r-   r   	file_hash[   s
    r:   FTboolzUnion[bool, dict[str, Any]])ignore_hashinfoverify	read_onlyverbosepre_scanr1   c          
     s*  | t jjkrdS t jj|  }t j |   |r:  s:dS |d}|rL|rXt j| }d|krvdd |d D |d<  fddt|d |d D }|s|rtdd	 |D rd
S |rdS  j	d
d
d  
d|  jdd"}	t|	 t|}
W dQ R X W dQ R X |rt|
sdS |
|d< |S )a  Check for the presence or integrity of the local copy of the specified
    test dataset. If the dataset is not available or out of date then attempt
    to download/update it transparently.

    :param pre_scan:         If all files are present and all file sizes match
                             then skip file integrity check and exit quicker.
    :param read_only:        Only use existing data, never download anything.
                             Implies pre_scan=True.
    :param verbose:          Show everything as it happens.
    :param verify:           Check all files against integrity information and
                             fail on any mismatch.
    :returns:                False if the dataset can not be downloaded/updated
                             for any reason.
                             True if the dataset is present and passes a
                             cursory inspection.
                             A validation dictionary if the dataset is present
                             and was fully verified.
    Fhashinfor=   c             S  s   g | ]}i qS r   r   ).0_r   r   r   
<listcomp>   s    z!fetch_dataset.<locals>.<listcomp>datac          	     s>   g | ]6\}}|d   t jt|d  j |d|dqS )r    files)r    filerF   r=   )ospathbasenamer   r'   )rB   sourcerA   )
target_dirr   r   rD      s   c             s  sB   | ]:}|d    o8|d do8|d d |d   jkV  qdS )rG   r=   sizeN)is_filer'   statst_size)rB   itemr   r   r   	<genexpr>   s   z fetch_dataset.<locals>.<genexpr>T)r#   r$   z.lock.w)r&   N)
dials_datadatasets
definitionrepository_locationis_dirr'   Zcreate_integrity_recordzipallr)   Z	with_namer*   r   _fetch_filelist)Zdatasetr<   r=   r>   r?   r@   rV   Zintegrity_infofilelistZfhZverification_recordsr   )rL   r   fetch_datasetd   s8    



r]   zlist[dict[str, Any]]zlist[dict[str, Any] | None])r\   r1   c             C  sx   t  f}ttddddddddd	hd
d}|d| |d| tjjdd}|t	
t|| }t|S Q R X d S )N   r   Ti  i  i  i  i  i  )ZtotalZbackoff_factorZraise_on_statusZstatus_forcelist)Zmax_retrieszhttp://zhttps://)max_workers)requestsZSessionr   r	   Zmount
concurrentZfuturesZThreadPoolExecutormap	functoolspartial_fetch_filelist)r\   ZrsZretry_adapterZpoolresultsr   r   r   r[      s    
r[   zdict[str, Any]zdict[str, Any] | None)r   rK   r1   c               s|  d}|d   rXd}|d rX|d d |d  jkr<d}n|d d t|d krXd}d}|std|d   t| |d |d  d}|d  jt|d d	}d}|d r$|d d |d krtd
|d  d|d  d|d d   d}n,|d d |d kr$td|d   d}|d rj|d j |sZt fdd|d D sjtd|d   |d jdkrt	
|d b}y,x&|d D ]}|j||d jd qW W n. tk
r   td| d|d   Y nX W d Q R X nvt|d b}xZ|d D ]N}y|j||d jd W n. tk
rX   td| d|d   Y nX qW W d Q R X |rt|S d S d S )NFrG   Tr=   rM   hashzDownloading r    )rM   rh   zFile size mismatch on z: z, expected zFile hash mismatch on rF   c             3  s   | ]} |   V  qd S )N)rN   )rB   r-   )rL   r   r   rR      s    z_fetch_file.<locals>.<genexpr>zDecompressing z.zip)rI   zExpected file z not present in zip archive z not present in tar archive )rN   rO   rP   r:   printr/   r(   rZ   suffixzipfileZZipFileextractKeyErrortarfiler*   )r   rK   ZvalidZ
downloadedZvalidation_recordZzfr-   Ztarr   )rL   r   re      s\    
(

"(,re   c               @  sP   e Zd ZdZdddZddddZd	d
 ZdddddZdddddZdS )DataFetchera  A class that offers access to regression datasets.

    To initialize:
        df = DataFetcher()
    Then
        df('insulin')
    returns a Path object to the insulin data. If that data is not already
    on disk it is downloaded automatically.

    To disable all downloads:
        df = DataFetcher(read_only=True)

    Do not use this class directly in tests! Use the dials_data fixture.
    Fc             C  s,   i | _ tj | _|o$t| jtj| _d S )N)	_cacherT   rU   rW   _target_dirrH   accessW_OK
_read_only)selfr>   r   r   r   __init__  s    zDataFetcher.__init__r   )r1   c             C  s   d | jrdnd| jS )Nz<{}DataFetcher: {}>zR/O  )formatrt   rq   )ru   r   r   r   __repr__  s    zDataFetcher.__repr__c             K  s   |S )z
        An overridable function to mangle lookup results.
        Used in tests to transform negative lookups to test skips.
        Overriding functions should add **kwargs to function signature
        to be forwards compatible.
        r   )ru   resultkwargsr   r   r   result_filter%  s    zDataFetcher.result_filterN)	test_datac             K  sx   || j kr| || j |< |dkr2tjdtdd | j | sH| jddS |sf| jtj| j | dS | j| j | dS )a  
        Return the location of a dataset, transparently downloading it if
        necessary and possible.
        The return value can be manipulated by overriding the result_filter
        function.
        :param test_data: name of the requested dataset.
        :param pathlib: Whether to return the result as a Python pathlib object.
                        The default for this setting is 'False' for now (leading
                        to a py.path.local object being returned), but the default
                        will change to 'True' in a future dials.data release.
                        Set to 'True' for forward compatibility.
        :return: A pathlib or py.path.local object pointing to the dataset, or False
                 if the dataset is not available.
        NzThe DataFetcher currently returns py.path.local() objects. This will in the future change to pathlib.Path() objects. You can either add a pathlib=True argument to obtain a pathlib.Path() object, or pathlib=False to silence this warning for now.   )
stacklevelF)rz   )	rp   _attempt_fetchwarningswarnDeprecationWarningr|   pyrI   Zlocal)ru   r}   pathlibr{   r   r   r   __call__.  s    

zDataFetcher.__call__zOptional[Path])r}   r1   c             C  s:   | j rt|ddd}nt|ddd}|r2| j| S d S d S )NT)r@   r>   F)rt   r]   rq   )ru   r}   Zdata_availabler   r   r   r   N  s    
zDataFetcher._attempt_fetch)F)N)	__name__
__module____qualname____doc__rv   ry   r|   r   r   r   r   r   r   ro   
  s   
	 ro   )FFFFT)*Z
__future__r   concurrent.futuresra   
contextlibr   rc   r6   rH   rn   r   rk   r   r   typingr   r   r   Zurllib.parser   Zpy.pathr   r`   Zrequests.adaptersr   Zurllib3.util.retryr	   Zdials_data.datasetsrT   namer   r   r   r   contextmanagerr   r/   r:   r]   r[   re   ro   r   r   r   r   <module>   sL   



    HF