B
    îq\éÃ  ã               @   s  d 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
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mZ ddlmZ ddlmZ ddlmZ ddlmZmZ dd	d
dddddddddddddddgZi ZG dd„ dej ƒZ!e!ƒ Z"G dd„ deƒZ#dd„ Z$dd„ Z%ej&d@d"d
„ƒZ'd#d„ Z(ej&dAd$d„ƒZ)dBd%d„Z*dCd&d„Z+dDd(d„Z,dEd)d„Z-d*d„ Z.dFd+d,„Z/d-d.„ Z0d/d„ Z1d0d„ Z2dGd1d„Z3d2d„ Z4d3d4„ Z5dHd5d„Z6g a7ej8d6d7„ ƒZ9dId8d„Z:d9d:„ Z;d;d<„ Z<d=d>„ Z=d?d„ Z>dS )Jz[ This module contains helper functions for accessing, downloading, and
caching data files.
é    N)ÚNamedTemporaryFileÚ
gettempdir)Úwarn)Úconfig)ÚAstropyWarning)Úfind_current_moduleÚresolve_nameÚConfÚconfÚget_readable_fileobjÚget_file_contentsÚget_pkg_data_fileobjÚget_pkg_data_filenameÚget_pkg_data_contentsÚget_pkg_data_fileobjsÚget_pkg_data_filenamesÚcompute_hashÚclear_download_cacheÚCacheMissingWarningÚget_free_space_in_dirÚcheck_free_space_in_dirÚdownload_fileÚdownload_files_in_parallelÚis_url_in_cacheÚget_cached_urlsc               @   sj   e Zd ZdZe dd¡Ze dd¡Zejdddgd	Ze d
d¡Z	e d
d¡Z
e dd¡Ze dd¡ZdS )r	   z<
    Configuration parameters for `astropy.utils.data`.
    zhttp://data.astropy.org/z)Primary URL for astropy remote data site.z$http://www.astropy.org/astropy-data/z(Mirror URL for astropy remote data site.g      $@z2Time to wait for remote data queries (in seconds).z5astropy.coordinates.name_resolve.name_resolve_timeout)Úaliasesi   z)Block size for computing MD5 file hashes.z4Number of bytes of remote data to download per step.é   zWNumber of times to try to get the lock while accessing the data cache before giving up.TzzIf True, temporary download files created when the cache is inaccessible will be deleted at the end of the python session.N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__Ú_configZ
ConfigItemÚdataurlÚdataurl_mirrorÚremote_timeoutÚcompute_hash_block_sizeÚdownload_block_sizeÚdownload_cache_lock_attemptsÚ"delete_temporary_downloads_at_exit© r)   r)   ú1lib/python3.7/site-packages/astropy/utils/data.pyr	   *   s.   
c               @   s   e Zd ZdZdS )r   a1  
    This warning indicates the standard cache directory is not accessible, with
    the first argument providing the warning message. If args[1] is present, it
    is a filename indicating the path to a temporary file that was created to
    store a remote data download in the absence of the cache.
    N)r   r   r   r    r)   r)   r)   r*   r   L   s   c             C   s   t j | ¡}|j ¡ dkS )zy
    Test whether a string is a valid URL

    Parameters
    ----------
    string : str
        The string to test
    )ZhttpZhttpsZftpZsftpZsshÚfile)ÚurllibÚparseZurlparseZschemeÚlower)ÚstringÚurlr)   r)   r*   Ú_is_urlU   s    	r1   c             C   s4   t j | ¡ t j |¡¡p2t j | ¡ t j |¡¡S )N)ÚosÚpathÚabspathÚ
startswithÚrealpath)r3   Úparent_pathr)   r)   r*   Ú
_is_insidee   s    r8   FTc          
   c   s´  t tjf}g }g }|dkr tj}t| |ƒrxt | ƒ} t| ƒ}|rNt| |||d} t 	| d¡}	|rl|sl| 
|	¡ | 
|	¡ n| }	t|	dƒs”t |	 ¡ ¡}	|	 d¡}
|	 d¡ |
dd… dkr*ddl}y$ddl}|j|	d	d
}| d¡ W n0 tt|jfk
r   |	 d¡ | ¡  Y nX | d¡ |}	nš|
dd… dkryddl}W n6 tk
r~   x|D ]}| ¡  q`W tdƒ‚Y nX yLtddd,}| |	 ¡ ¡ | ¡  |j|jd	d}W dQ R X | d¡ W n( tk
rô   |	 d¡ | ¡  Y nX | d¡ | 
|¡ |}	n´|
dd… dkrÄy$ddl}|j|	d	d}| d¡ W nn tk
r|   x|D ]}| ¡  q^W tdƒ‚Y nH ttfk
r´ } z|	 d¡ | ¡  W dd}~X Y nX | d¡ |}	|dk}|rnyddl}W n tk
rô   Y nXX t|	|jƒrLtddd}|	 ¡ }| |¡ | ¡  | 
|¡ t 	|jd¡}	| 
|	¡ t |	¡}	tj |	|d}	|	 d¡ z
|	V  W dx|D ]}| ¡  q€W x|D ]}t! "|j¡ q˜W X dS )a•  
    Given a filename, pathlib.Path object or a readable file-like object, return a context
    manager that yields a readable file-like object.

    This supports passing filenames, URLs, and readable file-like objects,
    any of which can be compressed in gzip, bzip2 or lzma (xz) if the
    appropriate compression libraries are provided by the Python installation.

    Notes
    -----

    This function is a context manager, and should be used for example
    as::

        with get_readable_fileobj('file.dat') as f:
            contents = f.read()

    Parameters
    ----------
    name_or_obj : str or file-like object
        The filename of the file to access (if given as a string), or
        the file-like object to access.

        If a file-like object, it must be opened in binary mode.

    encoding : str, optional
        When `None` (default), returns a file-like object with a
        ``read`` method that returns `str` (``unicode``) objects, using
        `locale.getpreferredencoding` as an encoding.  This matches
        the default behavior of the built-in `open` when no ``mode``
        argument is provided.

        When ``'binary'``, returns a file-like object where its ``read``
        method returns `bytes` objects.

        When another string, it is the name of an encoding, and the
        file-like object's ``read`` method will return `str` (``unicode``)
        objects, decoded from binary using the given encoding.

    cache : bool, optional
        Whether to cache the contents of remote URLs.

    show_progress : bool, optional
        Whether to display a progress bar if the file is downloaded
        from a remote server.  Default is `True`.

    remote_timeout : float
        Timeout for remote requests in seconds (default is the configurable
        `astropy.utils.data.Conf.remote_timeout`, which is 3s by default)

    Returns
    -------
    file : readable file-like object
    N)ÚcacheÚshow_progressÚtimeoutÚrÚseeké   r   é   s   ‹Úrb)ÚfileobjÚmodeé   s   BZhz`.bz2 format files are not supported since the Python interpreter does not include the bz2 moduleÚwbF)Údelete)rB   s   ý7zza.xz format files are not supported since the Python interpreter does not include the lzma module.Zbinary)Úencoding)#ÚstrÚpathlibZPathr
   r$   Ú
isinstancer1   r   ÚioÚFileIOÚappendÚhasattrÚBytesIOÚreadr=   ÚstructÚgzipZGzipFileÚOSErrorÚEOFErrorÚerrorÚcloseÚbz2ÚImportErrorÚ
ValueErrorr   ÚwriteZBZ2FileÚnameÚlzmaZLZMAFileÚBufferedReaderÚTextIOWrapperr2   Úremove)Zname_or_objrF   r9   r:   r$   Z
PATH_TYPESZ	close_fdsZ
delete_fdsZis_urlrA   Z	signaturerP   rQ   Zfileobj_newrV   ÚfdZtmpr[   ÚeZneeds_textio_wrapperÚdatar)   r)   r*   r   n   s´    @























c           	   O   s   t | |Ž
}| ¡ S Q R X dS )zó
    Retrieves the contents of a filename or file-like object.

    See  the `get_readable_fileobj` docstring for details on parameters.

    Returns
    -------
    content
        The content of the file (as requested by ``encoding``).

    N)r   rO   )ÚargsÚkwargsÚfr)   r)   r*   r   A  s    c       	   
   c   sì   t | |d}tj |¡r"tdƒ‚nÆtj |¡rNt||d}|V  W dQ R X nštjtj	f}xŒ|D ]\}y>t||  ||d"}| 
d¡ | d¡ |V  P W dQ R X W q` tjjk
rº   Y q`X q`W d d	d
„ |D ƒ¡}tj d | |¡¡‚dS )aî  
    Retrieves a data file from the standard locations for the package and
    provides the file as a file-like object that reads bytes.

    Parameters
    ----------
    data_name : str
        Name/location of the desired data file.  One of the following:

            * The name of a data file included in the source
              distribution.  The path is relative to the module
              calling this function.  For example, if calling from
              ``astropy.pkname``, use ``'data/file.dat'`` to get the
              file in ``astropy/pkgname/data/file.dat``.  Double-dots
              can be used to go up a level.  In the same example, use
              ``'../data/file.dat'`` to get ``astropy/data/file.dat``.
            * If a matching local file does not exist, the Astropy
              data server will be queried for the file.
            * A hash like that produced by `compute_hash` can be
              requested, prefixed by 'hash/'
              e.g. 'hash/34c33b3eb0d56eb9462003af249eff28'.  The hash
              will first be searched for locally, and if not found,
              the Astropy data server will be queried.

    package : str, optional
        If specified, look for a file relative to the given package, rather
        than the default of looking relative to the calling module's package.

    encoding : str, optional
        When `None` (default), returns a file-like object with a
        ``read`` method returns `str` (``unicode``) objects, using
        `locale.getpreferredencoding` as an encoding.  This matches
        the default behavior of the built-in `open` when no ``mode``
        argument is provided.

        When ``'binary'``, returns a file-like object where its ``read``
        method returns `bytes` objects.

        When another string, it is the name of an encoding, and the
        file-like object's ``read`` method will return `str` (``unicode``)
        objects, decoded from binary using the given encoding.

    cache : bool
        If True, the file will be downloaded and saved locally or the
        already-cached local copy will be accessed. If False, the
        file-like object will directly access the resource (e.g. if a
        remote URL is accessed, an object like that from
        `urllib.request.urlopen` is returned).

    Returns
    -------
    fileobj : file-like
        An object with the contents of the data file available via
        ``read`` function.  Can be used as part of a ``with`` statement,
        automatically closing itself after the ``with`` block.

    Raises
    ------
    urllib2.URLError, urllib.error.URLError
        If a remote file cannot be found.
    OSError
        If problems occur writing or reading a local file.

    Examples
    --------

    This will retrieve a data file and its contents for the `astropy.wcs`
    tests::

        >>> from astropy.utils.data import get_pkg_data_fileobj
        >>> with get_pkg_data_fileobj('data/3d_cd.hdr',
        ...                           package='astropy.wcs.tests') as fobj:
        ...     fcontents = fobj.read()
        ...

    This next example would download a data file from the astropy data server
    because the ``allsky/allsky_rosat.fits`` file is not present in the
    source distribution.  It will also save the file locally so the
    next time it is accessed it won't need to be downloaded.::

        >>> from astropy.utils.data import get_pkg_data_fileobj
        >>> with get_pkg_data_fileobj('allsky/allsky_rosat.fits',
        ...                           encoding='binary') as fobj:  # doctest: +REMOTE_DATA +IGNORE_OUTPUT
        ...     fcontents = fobj.read()
        ...
        Downloading http://data.astropy.org/allsky/allsky_rosat.fits [Done]

    This does the same thing but does *not* cache it locally::

        >>> with get_pkg_data_fileobj('allsky/allsky_rosat.fits',
        ...                           encoding='binary', cache=False) as fobj:  # doctest: +REMOTE_DATA +IGNORE_OUTPUT
        ...     fcontents = fobj.read()
        ...
        Downloading http://data.astropy.org/allsky/allsky_rosat.fits [Done]

    See Also
    --------
    get_pkg_data_contents : returns the contents of a file or url as a bytes object
    get_pkg_data_filename : returns a local name for a file containing the data
    )ÚpackagezDTried to access a data file that's actually a package data directory)rF   N)rF   r9   rC   r   Ú
c             s   s   | ]}d   |¡V  qdS )z  - {0}N)Úformat)Ú.0r0   r)   r)   r*   ú	<genexpr>Í  s    z'get_pkg_data_fileobj.<locals>.<genexpr>z<Failed to download {0} from the following repositories:

{1})Ú_find_pkg_data_pathr2   r3   ÚisdirrR   Úisfiler   r
   r"   r#   rO   r=   r,   rT   ÚURLErrorÚjoinrg   )	Ú	data_namere   rF   r9   ÚdatafnrA   Úall_urlsr0   Úurlsr)   r)   r*   r   Q  s(    g







c       
   	   C   s\  |dkrt j}|  d¡r¨t| dd… ƒ}|dkr¢t jt jf}x:|D ]2}yt||  d||dS  tjj	k
rr   Y qBX qBW d 
dd„ |D ƒ¡}tj 	d	 | |¡¡‚n|S n°tj | ¡}t||d
}	tj |	¡rÖtdƒ‚n‚tj |	¡ræ|	S t jt jf}x<|D ]4}yt||  d||dS  tjj	k
r*   Y qøX qøW d 
dd„ |D ƒ¡}tj 	d | |¡¡‚dS )aA  
    Retrieves a data file from the standard locations for the package and
    provides a local filename for the data.

    This function is similar to `get_pkg_data_fileobj` but returns the
    file *name* instead of a readable file-like object.  This means
    that this function must always cache remote files locally, unlike
    `get_pkg_data_fileobj`.

    Parameters
    ----------
    data_name : str
        Name/location of the desired data file.  One of the following:

            * The name of a data file included in the source
              distribution.  The path is relative to the module
              calling this function.  For example, if calling from
              ``astropy.pkname``, use ``'data/file.dat'`` to get the
              file in ``astropy/pkgname/data/file.dat``.  Double-dots
              can be used to go up a level.  In the same example, use
              ``'../data/file.dat'`` to get ``astropy/data/file.dat``.
            * If a matching local file does not exist, the Astropy
              data server will be queried for the file.
            * A hash like that produced by `compute_hash` can be
              requested, prefixed by 'hash/'
              e.g. 'hash/34c33b3eb0d56eb9462003af249eff28'.  The hash
              will first be searched for locally, and if not found,
              the Astropy data server will be queried.

    package : str, optional
        If specified, look for a file relative to the given package, rather
        than the default of looking relative to the calling module's package.

    show_progress : bool, optional
        Whether to display a progress bar if the file is downloaded
        from a remote server.  Default is `True`.

    remote_timeout : float
        Timeout for the requests in seconds (default is the
        configurable `astropy.utils.data.Conf.remote_timeout`, which
        is 3s by default)

    Raises
    ------
    urllib2.URLError, urllib.error.URLError
        If a remote file cannot be found.
    OSError
        If problems occur writing or reading a local file.

    Returns
    -------
    filename : str
        A file path on the local file system corresponding to the data
        requested in ``data_name``.

    Examples
    --------

    This will retrieve the contents of the data file for the `astropy.wcs`
    tests::

        >>> from astropy.utils.data import get_pkg_data_filename
        >>> fn = get_pkg_data_filename('data/3d_cd.hdr',
        ...                            package='astropy.wcs.tests')
        >>> with open(fn) as f:
        ...     fcontents = f.read()
        ...

    This retrieves a data file by hash either locally or from the astropy data
    server::

        >>> from astropy.utils.data import get_pkg_data_filename
        >>> fn = get_pkg_data_filename('hash/34c33b3eb0d56eb9462003af249eff28')  # doctest: +SKIP
        >>> with open(fn) as f:
        ...     fcontents = f.read()
        ...

    See Also
    --------
    get_pkg_data_contents : returns the contents of a file or url as a bytes object
    get_pkg_data_fileobj : returns a file-like object with the data
    Nzhash/r   T)r9   r:   r;   rf   c             s   s   | ]}d   |¡V  qdS )z  - {0}N)rg   )rh   r0   r)   r)   r*   ri   8  s    z(get_pkg_data_filename.<locals>.<genexpr>z>Failed to download {0} from the following repositories:

{1}

)re   zDTried to access a data file that's actually a package data directoryc             s   s   | ]}d   |¡V  qdS )z  - {0}N)rg   )rh   r0   r)   r)   r*   ri   O  s    z<Failed to download {0} from the following repositories:

{1})r
   r$   r5   Ú_find_hash_fnr"   r#   r   r,   rT   rm   rn   rg   r2   r3   Únormpathrj   rk   rR   rl   )
ro   re   r:   r$   Úhashfnrq   r0   rr   Zfs_pathrp   r)   r)   r*   r   Ò  sD    U









c          	   C   s(   t | |||d}| ¡ }W dQ R X |S )aí
  
    Retrieves a data file from the standard locations and returns its
    contents as a bytes object.

    Parameters
    ----------
    data_name : str
        Name/location of the desired data file.  One of the following:

            * The name of a data file included in the source
              distribution.  The path is relative to the module
              calling this function.  For example, if calling from
              ``astropy.pkname``, use ``'data/file.dat'`` to get the
              file in ``astropy/pkgname/data/file.dat``.  Double-dots
              can be used to go up a level.  In the same example, use
              ``'../data/file.dat'`` to get ``astropy/data/file.dat``.
            * If a matching local file does not exist, the Astropy
              data server will be queried for the file.
            * A hash like that produced by `compute_hash` can be
              requested, prefixed by 'hash/'
              e.g. 'hash/34c33b3eb0d56eb9462003af249eff28'.  The hash
              will first be searched for locally, and if not found,
              the Astropy data server will be queried.
            * A URL to some other file.

    package : str, optional
        If specified, look for a file relative to the given package, rather
        than the default of looking relative to the calling module's package.


    encoding : str, optional
        When `None` (default), returns a file-like object with a
        ``read`` method that returns `str` (``unicode``) objects, using
        `locale.getpreferredencoding` as an encoding.  This matches
        the default behavior of the built-in `open` when no ``mode``
        argument is provided.

        When ``'binary'``, returns a file-like object where its ``read``
        method returns `bytes` objects.

        When another string, it is the name of an encoding, and the
        file-like object's ``read`` method will return `str` (``unicode``)
        objects, decoded from binary using the given encoding.

    cache : bool
        If True, the file will be downloaded and saved locally or the
        already-cached local copy will be accessed. If False, the
        file-like object will directly access the resource (e.g. if a
        remote URL is accessed, an object like that from
        `urllib.request.urlopen` is returned).

    Returns
    -------
    contents : bytes
        The complete contents of the file as a bytes object.

    Raises
    ------
    urllib2.URLError, urllib.error.URLError
        If a remote file cannot be found.
    OSError
        If problems occur writing or reading a local file.

    See Also
    --------
    get_pkg_data_fileobj : returns a file-like object with the data
    get_pkg_data_filename : returns a local name for a file containing the data
    )re   rF   r9   N)r   rO   )ro   re   rF   r9   r_   Úcontentsr)   r)   r*   r   T  s    F
Ú*c             c   sl   t | |d}tj |¡r"tdƒ‚nFtj |¡r`x8t |¡D ] }t ||¡r:tj ||¡V  q:W ntdƒ‚dS )aÝ  
    Returns the path of all of the data files in a given directory
    that match a given glob pattern.

    Parameters
    ----------
    datadir : str
        Name/location of the desired data files.  One of the following:

            * The name of a directory included in the source
              distribution.  The path is relative to the module
              calling this function.  For example, if calling from
              ``astropy.pkname``, use ``'data'`` to get the
              files in ``astropy/pkgname/data``.
            * Remote URLs are not currently supported.

    package : str, optional
        If specified, look for a file relative to the given package, rather
        than the default of looking relative to the calling module's package.

    pattern : str, optional
        A UNIX-style filename glob pattern to match files.  See the
        `glob` module in the standard library for more information.
        By default, matches all files.

    Returns
    -------
    filenames : iterator of str
        Paths on the local filesystem in *datadir* matching *pattern*.

    Examples
    --------
    This will retrieve the contents of the data file for the `astropy.wcs`
    tests::

        >>> from astropy.utils.data import get_pkg_data_filenames
        >>> for fn in get_pkg_data_filenames('maps', 'astropy.wcs.tests',
        ...                                  '*.hdr'):
        ...     with open(fn) as f:
        ...         fcontents = f.read()
        ...
    )re   zDTried to access a data directory that's actually a package data filezPath not foundN)	rj   r2   r3   rl   rR   rk   ÚlistdirÚfnmatchrn   )Údatadirre   Úpatternr3   Úfilenamer)   r)   r*   r      s    ,c          
   c   s:   x4t | ||dD ]"}t||d}|V  W dQ R X qW dS )a|  
    Returns readable file objects for all of the data files in a given
    directory that match a given glob pattern.

    Parameters
    ----------
    datadir : str
        Name/location of the desired data files.  One of the following:

            * The name of a directory included in the source
              distribution.  The path is relative to the module
              calling this function.  For example, if calling from
              ``astropy.pkname``, use ``'data'`` to get the
              files in ``astropy/pkgname/data``
            * Remote URLs are not currently supported

    package : str, optional
        If specified, look for a file relative to the given package, rather
        than the default of looking relative to the calling module's package.

    pattern : str, optional
        A UNIX-style filename glob pattern to match files.  See the
        `glob` module in the standard library for more information.
        By default, matches all files.

    encoding : str, optional
        When `None` (default), returns a file-like object with a
        ``read`` method that returns `str` (``unicode``) objects, using
        `locale.getpreferredencoding` as an encoding.  This matches
        the default behavior of the built-in `open` when no ``mode``
        argument is provided.

        When ``'binary'``, returns a file-like object where its ``read``
        method returns `bytes` objects.

        When another string, it is the name of an encoding, and the
        file-like object's ``read`` method will return `str` (``unicode``)
        objects, decoded from binary using the given encoding.

    Returns
    -------
    fileobjs : iterator of file objects
        File objects for each of the files on the local filesystem in
        *datadir* matching *pattern*.

    Examples
    --------
    This will retrieve the contents of the data file for the `astropy.wcs`
    tests::

        >>> from astropy.utils.data import get_pkg_data_filenames
        >>> for fd in get_pkg_data_fileobjs('maps', 'astropy.wcs.tests',
        ...                                 '*.hdr'):
        ...     fcontents = fd.read()
        ...
    )re   r{   )rF   N)r   r   )rz   re   r{   rF   Úfnr_   r)   r)   r*   r   Ù  s    :c          	   C   sR   t | dƒ:}t ¡ }| tj¡}x|r>| |¡ | tj¡}q"W W dQ R X | ¡ S )ag   Computes the MD5 hash for a file.

    The hash for a data file is used for looking up data files in a unique
    fashion. This is of particular use for tests; a test may require a
    particular version of a particular file, in which case it can be accessed
    via hash to get the appropriate version.

    Typically, if you wish to write a test that requires a particular data
    file, you will want to submit that file to the astropy data servers, and
    use
    e.g. ``get_pkg_data_filename('hash/34c33b3eb0d56eb9462003af249eff28')``,
    but with the hash for your file in place of the hash in the example.

    Parameters
    ----------
    localfn : str
        The path to the file for which the hash should be generated.

    Returns
    -------
    md5hash : str
        The hex digest of the MD5 hash for the contents of the ``localfn``
        file.

    r@   N)ÚopenÚhashlibÚmd5rO   r
   r%   ÚupdateÚ	hexdigest)Zlocalfnrd   ÚhÚblockr)   r)   r*   r     s    
c             C   sÄ   |dkr`t dddgd}|dkr$| S t|dƒr4|jsXd|jkrP|j d¡d }q^|j}qh|j}nt|ƒ}| d¡d }t|ƒ}tj 	|j
¡}tj || ¡}tj 	|j
¡}t||ƒsÀtd	 |¡ƒ‚|S )
zX
    Look for data in the source-included data directories and return the
    path.
    NrC   zastropy.utils.dataÚ
contextlib)ZfinddiffÚ__package__Ú.r   z:attempted to get a local data file outside of the {} tree.)r   rM   r†   r   Ú
rpartitionr   Ú	partitionr2   r3   ÚdirnameÚ__file__rn   r8   ÚRuntimeErrorrg   )ro   re   ÚmoduleZrootpkgnameZrootpkgZmodule_pathr3   Zroot_dirr)   r)   r*   rj   >  s&    

rj   c          
   C   sp   yt ƒ \}}W n: tk
rH } zd}tt|t|ƒ ƒƒ dS d}~X Y nX tj || ¡}tj |¡rh|S dS dS )zs
    Looks for a local file by hash - returns file name if found and a valid
    file, otherwise returns None.
    z:Could not access cache directory to search for data file: N)	Ú_get_download_cache_locsrR   r   r   rG   r2   r3   rn   rl   )ÚhashÚdldirÚurlmapfnr`   Úmsgru   r)   r)   r*   rs   f  s    rs   c             C   st   t j d¡rZddl}| d¡}|jj | | ¡dd| 	|¡¡}|dkrTt
d | ¡ƒ‚|jS t | ¡}|j|j S dS )a5  
    Given a path to a directory, returns the amount of free space (in
    bytes) on that filesystem.

    Parameters
    ----------
    path : str
        The path to a directory

    Returns
    -------
    bytes : int
        The amount of free space on the partition that the directory
        is on.
    Úwinr   Nz0Checking free space on {!r} failed unexpectedly.)ÚsysÚplatformr5   ÚctypesZc_ulonglongZwindllZkernel32ZGetDiskFreeSpaceExWZ	c_wchar_pZpointerrR   rg   Úvaluer2   ÚstatvfsÚf_bavailÚf_frsize)r3   r–   Z
free_bytesZretvalÚstatr)   r)   r*   r   y  s    

c             C   s4   ddl m} t| ƒ}||k r0td | ||ƒ¡ƒ‚dS )ag  
    Determines if a given directory has enough space to hold a file of
    a given size.  Raises an OSError if the file would be too large.

    Parameters
    ----------
    path : str
        The path to a directory

    size : int
        A proposed filesize (in bytes)

    Raises
    -------
    OSError : There is not enough room on the filesystem
    r   )Úhuman_file_sizez5Not enough free space in '{0}' to download a {1} fileN)Úastropy.utils.consolerœ   r   rR   rg   )r3   Úsizerœ   Zspacer)   r)   r*   r   ˜  s    c             C   sV  ddl m} |dkrtj}d}|r–ytƒ \}}W nd tk
r” } zFd}	t|jƒdk rXdn
dt|ƒ }
t	t
|	|jj |
 ƒƒ d}d	}W dd}~X Y nX | }| tj¡rtjtkry6tjjtj|d
}tj| ¡ gttj< W dQ R X W n( tjjk
r   tjgttj< Y nX yŽ|rŒt |¡^}||kr<|| S xDt tjg ¡D ]2}| |¡rL| |tj¡}||krL|| S qLW W dQ R X tjj| |d
b}t ¡ }| ¡ }d|krèyt|d ƒ}W n tk
rä   d}Y nX nd}|dk	rt t!ƒ |ƒ |rt ||ƒ |r,t"j# $¡ r,t"j#}nt% &¡ }d '| ¡}||||dª}t(dd”}yXd}| )tj*¡}xB|r®| +|¡ | ,|¡ |t|ƒ7 }| ,|¡ | )tj*¡}qnW W n4 t-k
ræ   t.j/ 0|j1¡ràt. 2|j1¡ ‚ Y nX W dQ R X W dQ R X W dQ R X |rrt3ƒ  zTt |¡@}||kr2|| S t.j/ 4|| 5¡ ¡}t6 7|j1|¡ |||< W dQ R X W dt8ƒ  X n0|j1}|rd}	t	t
|	|ƒƒ tj9r¢t: ;|¡ W n¬ tjjk
r  } zXt<|dƒrt<|j=dƒr|j=j>dkr|j=j?d |  |j=_?|j=j>|j=j?f|j=_|‚W dd}~X Y n2 t@jAk
rP } ztj |¡‚W dd}~X Y nX |S )aÔ  
    Accepts a URL, downloads and optionally caches the result
    returning the filename, with a name determined by the file's MD5
    hash. If ``cache=True`` and the file is present in the cache, just
    returns the filename.

    Parameters
    ----------
    remote_url : str
        The URL of the file to download

    cache : bool, optional
        Whether to use the cache

    show_progress : bool, optional
        Whether to display a progress bar during the download (default
        is `True`). Regardless of this setting, the progress bar is only
        displayed when outputting to a terminal.

    timeout : float, optional
        The timeout, in seconds.  Otherwise, use
        `astropy.utils.data.Conf.remote_timeout`.

    Returns
    -------
    local_path : str
        Returns the local path that the file was download to.

    Raises
    ------
    urllib2.URLError, urllib.error.URLError
        Whenever there's a problem getting the remote file.
    r   )ÚProgressBarOrSpinnerNFz/Remote data cache could not be accessed due to rC   Ú z: T)r;   zContent-LengthzDownloading {0})r+   )rE   zaFile downloaded to temporary location due to problem with cache directory and will not be cached.ÚreasonÚerrnoé   z. requested URL: )Br   rŸ   r
   r$   rŽ   rR   Úlenrb   rG   r   r   Ú	__class__r   r5   r"   Ú_dataurls_to_aliasr,   ZrequestZurlopenZgeturlrT   rm   Úshelver~   ÚgetÚreplacer#   r   r€   ÚinfoÚintrX   r   r   r”   ÚstdoutÚisattyrJ   ÚStringIOrg   r   rO   r&   rY   r   ÚBaseExceptionr2   r3   ÚexistsrZ   r^   Ú_acquire_download_cache_lockrn   r‚   ÚshutilZmoveÚ_release_download_cache_lockr(   Ú_tempfilestodelrL   rM   r¡   r¢   ÚstrerrorÚsocketr;   )Z
remote_urlr9   r:   r;   rŸ   Zmissing_cacher   r‘   r`   r’   ÚestrÚurl_keyZremoteÚurl2hashZcur_urlZ
url_mirrorr   rª   rž   Zprogress_streamZdlmsgÚprd   Z
bytes_readr„   Z
local_pathr)   r)   r*   r   ³  s°    #"








&

(c          
   C   s’   yt ƒ \}}W n\ tk
rj } z>d}t|jƒdk r6dn
dt|ƒ }tt||jj | ƒƒ dS d}~X Y nX t	 
|¡}| |kr„dS W dQ R X dS )zø
    Check if a download from ``url_key`` is in the cache.

    Parameters
    ----------
    url_key : string
        The URL retrieved

    Returns
    -------
    in_cache : bool
        `True` if a download from ``url_key`` is in the cache
    z/Remote data cache could not be accessed due to rC   r    z: FNT)rŽ   rR   r¤   rb   rG   r   r   r¥   r   r§   r~   )r¸   r   r‘   r`   r’   r·   r¹   r)   r)   r*   r   N  s    c             C   s   t | Ž S )N)r   )rb   r)   r)   r*   Ú_do_download_files_in_parallelk  s    r»   c       
         s˜   ddl m} ˆdkrtj‰ˆ s,tdtƒ d‰ |r8tj}nt 	¡ }t
t| ƒƒ}|jt‡ ‡fdd„|D ƒ|dd}g }x | D ]}	| || |	¡ ¡ qxW |S )	añ  
    Downloads multiple files in parallel from the given URLs.  Blocks until
    all files have downloaded.  The result is a list of local file paths
    corresponding to the given urls.

    Parameters
    ----------
    urls : list of str
        The URLs to retrieve.

    cache : bool, optional
        Whether to use the cache (default is `True`).

        .. versionchanged:: 3.0
            The default was changed to ``True`` and setting it to ``False`` will
            print a Warning and set it to ``True`` again, because the function
            will not work properly without cache.

    show_progress : bool, optional
        Whether to display a progress bar during the download (default
        is `True`)

    timeout : float, optional
        Timeout for each individual requests in seconds (default is the
        configurable `astropy.utils.data.Conf.remote_timeout`).

    Returns
    -------
    paths : list of str
        The local file paths corresponding to the downloaded URLs.
    rC   )ÚProgressBarNz–Disabling the cache does not work because of multiprocessing, it will be set to ``True``. You may need to manually remove the cached files afterwards.Tc                s   g | ]}|ˆ d ˆf‘qS )Fr)   )rh   Úx)r9   r;   r)   r*   ú
<listcomp>¨  s    z.download_files_in_parallel.<locals>.<listcomp>)r+   Zmultiprocess)Zconsoler¼   r
   r$   r   r   r”   r¬   rJ   rN   ÚlistÚsetÚmapr»   rL   Úindex)
rr   r9   r:   r;   r¼   ZprogressZcombined_urlsZcombined_pathsÚpathsr0   r)   )r9   r;   r*   r   o  s(    !
c              C   s<   t d k	r8x.tt ƒdkr6t  ¡ } tj | ¡r
t | ¡ q
W d S )Nr   )r´   r¤   Úpopr2   r3   rl   r^   )r}   r)   r)   r*   Ú	_deltemps¶  s
    rÅ   c             C   sd  yt ƒ \}}W n\ tk
rj } z>d}t|jƒdk r6dn
dt|ƒ }tt||jj | ƒƒ dS d}~X Y nX t	ƒ  zÌ| dkr”t
j |¡r’t |¡ n¨t |¡˜}t
j || ¡}t||ƒsÀtdƒ‚| }t
j |¡rx"| ¡ D ]\}	}
|
|krÜ||	= qÜW t
 |¡ n0||kr2|| }||= t
j |¡r2t
 |¡ W dQ R X W dt
j t
j |d¡¡r^tƒ  X dS )aJ   Clears the data file cache by deleting the local file(s).

    Parameters
    ----------
    hashorurl : str or None
        If None, the whole cache is cleared.  Otherwise, either specifies a
        hash for the cached file that is supposed to be deleted, or a URL that
        should be removed from the cache if present.
    z3Not clearing data cache - cache inacessable due to rC   r    z: NzPattempted to use clear_download_cache on a path outside the data cache directoryÚlock)rŽ   rR   r¤   rb   rG   r   r   r¥   r   r±   r2   r3   r°   r²   Zrmtreer§   r~   rn   r8   rŒ   ÚitemsÚunlinkr³   )Z	hashorurlr   r‘   r`   r’   r·   r¹   ÚfilepathZhash_keyÚkÚvr)   r)   r*   r   Â  s:    


c           
   C   sÎ   ddl m}  dttjjƒ }tj | ƒ d|¡}tj |d¡}tj 	|¡sŠyt 
|¡ W q¨ tk
r† } ztj 	|¡sv‚ W dd}~X Y q¨X ntj |¡s¨d}t| |¡ƒ‚tj |¡rÆd}t| |¡ƒ‚||fS )	a   Finds the path to the data cache directory and makes them if
    they don't exist.

    Returns
    -------
    datadir : str
        The path to the data cache directory.
    shelveloc : str
        The path to the shelve object that stores the cache info.
    r   )Úget_cache_dirÚpyZdownloadZurlmapNz+Data cache directory {0} is not a directoryz4Data cache shelve object location {0} is a directory)Zastropy.config.pathsrÌ   rG   r”   Úversion_infoÚmajorr2   r3   rn   r°   ÚmakedirsrR   rk   rg   )rÌ   Z
py_versionrz   Z	shelvelocr`   r’   r)   r)   r*   rŽ   û  s"    rŽ   c           
   C   s¢   t j tƒ d d¡} xvttjƒD ]h}y@t  | ¡ tt j | d¡dƒ}| 	t
t  ¡ ƒ¡ W dQ R X W n tk
r‚   t d¡ Y q X dS q W d}t| | ¡ƒ‚dS )z„
    Uses the lock directory method.  This is good because `mkdir` is
    atomic at the system call level, so it's thread-safe.
    r   rÆ   ÚpidÚwNrC   zUnable to acquire lock for cache directory ({0} exists). You may need to delete the lock if the python interpreter wasn't shut down properly.)r2   r3   rn   rŽ   Úranger
   r'   Úmkdirr~   rY   rG   ÚgetpidrR   ÚtimeZsleeprŒ   rg   )ÚlockdirÚird   r’   r)   r)   r*   r±   $  s    
 r±   c              C   sf   t j tƒ d d¡} t j | ¡rPt j | d¡}t j |¡rDt  |¡ t  | ¡ nd}t| 	| ¡ƒ‚d S )Nr   rÆ   rÑ   zHError releasing lock. "{0}" either does not exist or is not a directory.)
r2   r3   rn   rŽ   rk   r°   r^   ÚrmdirrŒ   rg   )r×   Zpidfnr’   r)   r)   r*   r³   <  s    
r³   c           
   C   sŽ   yt ƒ \} }W n\ tk
rj } z>d}t|jƒdk r6dn
dt|ƒ }tt||jj | ƒƒ dS d}~X Y nX t	 
|¡}t| ¡ ƒS Q R X dS )zç
    Get the list of URLs in the cache. Especially useful for looking up what
    files are stored in your cache when you don't have internet access.

    Returns
    -------
    cached_urls : list
        List of cached URLs.
    z/Remote data cache could not be accessed due to rC   r    z: FN)rŽ   rR   r¤   rb   rG   r   r   r¥   r   r§   r~   r¿   Úkeys)r   r‘   r`   r’   r·   r¹   r)   r)   r*   r   K  s    )NFTN)NNT)NTN)NNT)Nrw   )Nrw   N)N)FTN)TTN)N)?r    Úatexitr…   ry   r   r2   rJ   rH   r²   r¶   r”   rÖ   Zurllib.requestr,   Zurllib.errorZurllib.parser§   Ztempfiler   r   Úwarningsr   Zastropyr   r!   Zastropy.utils.exceptionsr   Zastropy.utils.introspectionr   r   Ú__all__r¦   ZConfigNamespacer	   r
   r   r1   r8   Úcontextmanagerr   r   r   r   r   r   r   r   rj   rs   r   r   r   r   r»   r   r´   ÚregisterrÅ   r   rŽ   r±   r³   r   r)   r)   r)   r*   Ú<module>   s‚   
		  R  
 
L
9
@%
(
  
C
9)