o
    a&i^                     @   s   d Z ddlZddlZddlZddlmZ zddlmZ W n ey'   dZY nw zddlZW n ey9   dZY nw dddZ	G d	d
 d
Z
G dd dZG dd dZG dd dZdd Zdd Zdd ZdS )z1
The classes that actually handle the downloads.
    N   )	parse_url)tqdmFc                 C   s^   t ttttd}t| }|d |vr$td|d  d|  d|  d||d  |d}|S )a  
    Choose the appropriate downloader for the given URL based on the protocol.

    Parameters
    ----------
    url : str
        A URL (including protocol).
    progressbar : bool or an arbitrary progress bar object
        If True, will print a progress bar of the download to standard error
        (stderr). Requires `tqdm <https://github.com/tqdm/tqdm>`__ to be
        installed. Alternatively, an arbitrary progress bar object can be
        passed. See :ref:`custom-progressbar` for details.

    Returns
    -------
    downloader
        A downloader class, like :class:`pooch.HTTPDownloader`,
        :class:`pooch.FTPDownloader`, or :class: `pooch.SFTPDownloader`.

    Examples
    --------

    >>> downloader = choose_downloader("http://something.com")
    >>> print(downloader.__class__.__name__)
    HTTPDownloader
    >>> downloader = choose_downloader("https://something.com")
    >>> print(downloader.__class__.__name__)
    HTTPDownloader
    >>> downloader = choose_downloader("ftp://something.com")
    >>> print(downloader.__class__.__name__)
    FTPDownloader
    >>> downloader = choose_downloader("doi:DOI/filename.csv")
    >>> print(downloader.__class__.__name__)
    DOIDownloader

    )ftphttpshttpsftpdoiprotocolzUnrecognized URL protocol 'z' in ''. Must be one of .)progressbar)FTPDownloaderHTTPDownloaderSFTPDownloaderDOIDownloaderr   
ValueErrorkeys)urlr   Zknown_downloaders
parsed_url
downloader r   <C:\wamp64\www\opt\env\Lib\site-packages\pooch/downloaders.pychoose_downloader   s   &r   c                   @   "   e Zd ZdZd	ddZdd ZdS )
r   a  
    Download manager for fetching files over HTTP/HTTPS.

    When called, downloads the given file URL into the specified local file.
    Uses the :mod:`requests` library to manage downloads.

    Use with :meth:`pooch.Pooch.fetch` or :func:`pooch.retrieve` to customize
    the download of files (for example, to use authentication or print a
    progress bar).

    Parameters
    ----------
    progressbar : bool or an arbitrary progress bar object
        If True, will print a progress bar of the download to standard error
        (stderr). Requires `tqdm <https://github.com/tqdm/tqdm>`__ to be
        installed. Alternatively, an arbitrary progress bar object can be
        passed. See :ref:`custom-progressbar` for details.
    chunk_size : int
        Files are streamed *chunk_size* bytes at a time instead of loading
        everything into memory at one. Usually doesn't need to be changed.
    **kwargs
        All keyword arguments given when creating an instance of this class
        will be passed to :func:`requests.get`.

    Examples
    --------

    Download one of the data files from the Pooch repository:

    >>> import os
    >>> from pooch import __version__, check_version
    >>> url = "https://github.com/fatiando/pooch/raw/{}/data/tiny-data.txt"
    >>> url = url.format(check_version(__version__, fallback="main"))
    >>> downloader = HTTPDownloader()
    >>> # Not using with Pooch.fetch so no need to pass an instance of Pooch
    >>> downloader(url=url, output_file="tiny-data.txt", pooch=None)
    >>> os.path.exists("tiny-data.txt")
    True
    >>> with open("tiny-data.txt") as f:
    ...     print(f.read().strip())
    # A tiny data file for test purposes only
    1  2  3  4  5  6
    >>> os.remove("tiny-data.txt")

    Authentication can be handled by passing a user name and password to
    :func:`requests.get`. All arguments provided when creating an instance of
    the class are forwarded to :func:`requests.get`. We'll use
    ``auth=(username, password)`` to use basic HTTPS authentication. The
    https://httpbin.org website allows us to make a fake a login request using
    whatever username and password we provide to it:

    >>> user = "doggo"
    >>> password = "goodboy"
    >>> # httpbin will ask for the user and password we provide in the URL
    >>> url = f"https://httpbin.org/basic-auth/{user}/{password}"
    >>> # Trying without the login credentials causes an error
    >>> downloader = HTTPDownloader()
    >>> try:
    ...     downloader(url=url, output_file="tiny-data.txt", pooch=None)
    ... except Exception:
    ...     print("There was an error!")
    There was an error!
    >>> # Pass in the credentials to HTTPDownloader
    >>> downloader = HTTPDownloader(auth=(user, password))
    >>> downloader(url=url, output_file="tiny-data.txt", pooch=None)
    >>> with open("tiny-data.txt") as f:
    ...     for line in f:
    ...         print(line.rstrip())
    {
      "authenticated": true,
      "user": "doggo"
    }
    >>> os.remove("tiny-data.txt")

    F   c                 K   s4   || _ || _|| _| jdu rtd u rtdd S d S NT2Missing package 'tqdm' required for progress bars.)kwargsr   
chunk_sizer   r   selfr   r   r   r   r   r   __init__   s   zHTTPDownloader.__init__c                 C   s$  | j  }|dd t|d }|rt|d}zqtj|fi |}|  |j| j	d}t
|jdd}| jdu rMttjdk}	t|d	|	d
ddd}
n	| jrV| j}
||
_|D ]}|rn|| |  | jrn|
| j	 qX| jr|
  |
| |
  W |r|  dS dS |r|  w w )a  
        Download the given URL over HTTP to the given output file.

        Uses :func:`requests.get`.

        Parameters
        ----------
        url : str
            The URL to the file you want to download.
        output_file : str or file-like object
            Path (and file name) to which the file will be downloaded.
        pooch : :class:`~pooch.Pooch`
            The instance of :class:`~pooch.Pooch` that is calling this method.

        streamTwritew+b)r   zcontent-lengthr   win32O   BtotalZncolsasciiunitZ
unit_scaleZleaveN)r   copy
setdefaulthasattropenrequestsgetraise_for_statusiter_contentr   intheadersr   boolsysplatformr   r*   r$   flushupdateresetclose)r!   r   output_filepoochr   ispathresponsecontentr*   	use_asciiprogresschunkr   r   r   __call__   sP   





zHTTPDownloader.__call__NFr   __name__
__module____qualname____doc__r"   rF   r   r   r   r   r   S   s    
Lr   c                   @   s0   e Zd ZdZ							ddd	Zd
d ZdS )r   a  
    Download manager for fetching files over FTP.

    When called, downloads the given file URL into the specified local file.
    Uses the :mod:`ftplib` module to manage downloads.

    Use with :meth:`pooch.Pooch.fetch` or :func:`pooch.retrieve` to customize
    the download of files (for example, to use authentication or print a
    progress bar).

    Parameters
    ----------
    port : int
        Port used for the FTP connection.
    username : str
        User name used to login to the server. Only needed if the server
        requires authentication (i.e., no anonymous FTP).
    password : str
        Password used to login to the server. Only needed if the server
        requires authentication (i.e., no anonymous FTP). Use the empty string
        to indicate no password is required.
    account : str
        Some servers also require an "account" name for authentication.
    timeout : int
        Timeout in seconds for ftp socket operations, use None to mean no
        timeout.
    progressbar : bool
        If True, will print a progress bar of the download to standard error
        (stderr). Requires `tqdm <https://github.com/tqdm/tqdm>`__ to be
        installed. **Custom progress bars are not yet supported.**
    chunk_size : int
        Files are streamed *chunk_size* bytes at a time instead of loading
        everything into memory at one. Usually doesn't need to be changed.

       	anonymous NFr   c                 C   sL   || _ || _|| _|| _|| _|| _|| _| jdu r"td u r$tdd S d S r   )	portusernamepasswordaccounttimeoutr   r   r   r   )r!   rP   rQ   rR   rS   rT   r   r   r   r   r   r"     s   zFTPDownloader.__init__c                    s>  t |}tj| jd}|j|d | jd t d }|r"t d zp|j| j	| j
| jd d|d  }| jrz|d	 t||d }ttjd
k}	t|d|	dddd  fdd}
|j||
| jd W d   n1 stw   Y  n
|j| j| jd W |  |r   dS dS |  |r   w w )a  
        Download the given URL over FTP to the given output file.

        Parameters
        ----------
        url : str
            The URL to the file you want to download.
        output_file : str or file-like object
            Path (and file name) to which the file will be downloaded.
        pooch : :class:`~pooch.Pooch`
            The instance of :class:`~pooch.Pooch` that is calling this method.
        )rT   netloc)hostrP   r$   r%   )userpasswdZacctzRETR pathzTYPE Ir&   r'   r(   Tr)   c                    s    t|   |  dS z+Update the progress bar and write to outputN)r;   lenr$   )datar>   rD   r   r   callbackH  s   z(FTPDownloader.__call__.<locals>.callback)	blocksizeN)r   ftplibFTPrT   connectrP   r/   r0   loginrQ   rR   rS   r   voidcmdr5   sizer7   r8   r9   r   Z
retrbinaryr   r$   quitr=   )r!   r   r>   r?   r   r   r@   commandre   rC   r^   r   r]   r   rF   !  sH   

	
zFTPDownloader.__call__)rM   rN   rO   rO   NFr   rH   r   r   r   r   r      s    &
r   c                   @   s.   e Zd ZdZ						dddZd	d
 ZdS )r   a  
    Download manager for fetching files over SFTP.

    When called, downloads the given file URL into the specified local file.
    Requires `paramiko <https://github.com/paramiko/paramiko>`__ to be
    installed.

    Use with :meth:`pooch.Pooch.fetch` or :func:`pooch.retrieve` to customize
    the download of files (for example, to use authentication or print a
    progress bar).

    Parameters
    ----------
    port : int
        Port used for the SFTP connection.
    username : str
        User name used to login to the server. Only needed if the server
        requires authentication (i.e., no anonymous SFTP).
    password : str
        Password used to login to the server. Only needed if the server
        requires authentication (i.e., no anonymous SFTP). Use the empty
        string to indicate no password is required.
    timeout : int
        Timeout in seconds for sftp socket operations, use None to mean no
        timeout.
    progressbar : bool or an arbitrary progress bar object
        If True, will print a progress bar of the download to standard
        error (stderr). Requires `tqdm <https://github.com/tqdm/tqdm>`__ to
        be installed.

       rN   rO   NFc                 C   sh   || _ || _|| _|| _|| _|| _g }| jr td u r |d td u r)|d |r2t	d
|d S )Nr   z7Missing package 'paramiko' required for SFTP downloads. )rP   rQ   rR   rS   rT   r   r   appendparamikor   join)r!   rP   rQ   rR   rS   rT   r   errorsr   r   r   r"   w  s   	

zSFTPDownloader.__init__c           
         s$  t |}tj|d | jfd}d}zr|j| j| jd tj|}| j	|
 _| jrEt||d j}ttjdk}t|d|dd	d	d
 | jrk   fdd}	|j|d ||	d W d   n1 sew   Y  n||d | W |  |dur|  dS dS |  |dur|  w w )aQ  
        Download the given URL over SFTP to the given output file.

        The output file must be given as a string (file name/path) and not an
        open file object! Otherwise, paramiko cannot save to that file.

        Parameters
        ----------
        url : str
            The URL to the file you want to download.
        output_file : str
            Path (and file name) to which the file will be downloaded. **Cannot
            be a file object**.
        pooch : :class:`~pooch.Pooch`
            The instance of :class:`~pooch.Pooch` that is calling this method.
        rU   )sockN)rQ   rR   rY   r&   r'   r(   Tr)   c                    s"   t | _ t |  j  dS rZ   )r5   r*   r;   n)currentr*   rD   r   r   r^     s   
z)SFTPDownloader.__call__.<locals>.callback)r^   )r   rk   	TransportrP   rb   rQ   rR   Z
SFTPClientZfrom_transportrT   Zget_channel
settimeoutr   r5   statst_sizer7   r8   r9   r   r2   r=   )
r!   r   r>   r?   r   
connectionr   re   rC   r^   r   rq   r   rF     sD   	
zSFTPDownloader.__call__)rh   rN   rO   rO   NFrH   r   r   r   r   r   V  s    "
r   c                   @   r   )
r   a  
    Download manager for fetching files from Digital Object Identifiers (DOIs).

    Open-access data repositories often issue Digital Object Identifiers (DOIs)
    for data which provide a stable link and citation point. The trick is
    finding out the download URL for a file given the DOI.

    When called, this downloader uses the repository's public API to find out
    the download URL from the DOI and file name. It then uses
    :class:`pooch.HTTPDownloader` to download the URL into the specified local
    file. Allowing "URL"s  to be specified with the DOI instead of the actual
    HTTP download link. Uses the :mod:`requests` library to manage downloads
    and interact with the APIs.

    The **format of the "URL"** is: ``doi:{DOI}/{file name}``.

    Notice that there are no ``//`` like in HTTP/FTP and you must specify a
    file name after the DOI (separated by a ``/``).

    Use with :meth:`pooch.Pooch.fetch` or :func:`pooch.retrieve` to be able to
    download files given the DOI instead of an HTTP link.

    Supported repositories:

    * `figshare <https://www.figshare.com>`__
    * `Zenodo <https://www.zenodo.org>`__

    .. attention::

        DOIs from other repositories **will not work** since we need to access
        their particular APIs to find the download links. We welcome
        suggestions and contributions adding new repositories.

    Parameters
    ----------
    progressbar : bool or an arbitrary progress bar object
        If True, will print a progress bar of the download to standard error
        (stderr). Requires `tqdm <https://github.com/tqdm/tqdm>`__ to be
        installed. Alternatively, an arbitrary progress bar object can be
        passed. See :ref:`custom-progressbar` for details.
    chunk_size : int
        Files are streamed *chunk_size* bytes at a time instead of loading
        everything into memory at one. Usually doesn't need to be changed.
    **kwargs
        All keyword arguments given when creating an instance of this class
        will be passed to :func:`requests.get`.

    Examples
    --------

    Download one of the data files from the figshare archive of Pooch test
    data:

    >>> import os
    >>> downloader = DOIDownloader()
    >>> url = "doi:10.6084/m9.figshare.14763051.v1/tiny-data.txt"
    >>> # Not using with Pooch.fetch so no need to pass an instance of Pooch
    >>> downloader(url=url, output_file="tiny-data.txt", pooch=None)
    >>> os.path.exists("tiny-data.txt")
    True
    >>> with open("tiny-data.txt") as f:
    ...     print(f.read().strip())
    # A tiny data file for test purposes only
    1  2  3  4  5  6
    >>> os.remove("tiny-data.txt")

    Same thing but for our Zenodo archive:

    >>> url = "doi:10.5281/zenodo.4924875/tiny-data.txt"
    >>> downloader(url=url, output_file="tiny-data.txt", pooch=None)
    >>> os.path.exists("tiny-data.txt")
    True
    >>> with open("tiny-data.txt") as f:
    ...     print(f.read().strip())
    # A tiny data file for test purposes only
    1  2  3  4  5  6
    >>> os.remove("tiny-data.txt")

    Fr   c                 K   s   || _ || _|| _d S )N)r   r   r   r    r   r   r   r"     s   
zDOIDownloader.__init__c                 C   s   t td}t|}|d }t|}t|d }||vr*td| dt|  d|| ||d dd |d	}	td| j	| j
d
| j}
|
|	|| dS )a7  
        Download the given DOI URL over HTTP to the given output file.

        Uses the repository's API to determine the actual HTTP download URL
        from the given DOI.

        Uses :func:`requests.get`.

        Parameters
        ----------
        url : str
            The URL to the file you want to download.
        output_file : str or file-like object
            Path (and file name) to which the file will be downloaded.
        pooch : :class:`~pooch.Pooch`
            The instance of :class:`~pooch.Pooch` that is calling this method.

        )zfigshare.comz
zenodo.orgrU   zInvalid data repository 'r   zx. To request or contribute support for this repository, please open an issue at https://github.com/fatiando/pooch/issuesrY   /)archive_url	file_namer	   )r   r   Nr   )figshare_download_urlzenodo_download_urlr   
doi_to_urlr   listr   splitr   r   r   r   )r!   r   r>   r?   
convertersr   r	   ry   
repositorydownload_urlr   r   r   r   rF     s0   
zDOIDownloader.__call__NrG   rH   r   r   r   r   r     s    
Pr   c                 C   sL   t d|  }|j}d|j  krdk r$n |S td|  d| d|S )z
    Follow a DOI link to resolve the URL of the archive.

    Parameters
    ----------
    doi : str
        The DOI of the archive.

    Returns
    -------
    url : str
        The URL of the archive in the data repository.

    zhttps://doi.org/i  iX  zArchive with doi:z not found (see z). Is the DOI correct?)r1   r2   r   status_coder   )r	   rA   r   r   r   r   r}   H  s   r}   c                 C   sl   |  dd }td|  }dd |d D }||vr,td| d|  d	| d
|| d d }|S )  
    Use the API to get the download URL for a file given the archive URL.

    Parameters
    ----------
    archive_url : str
        URL of the dataset in the repository.
    file_name : str
        The name of the file in the archive that will be downloaded.
    doi : str
        The DOI of the archive.

    Returns
    -------
    download_url : str
        The HTTP URL that can be used to download the file.

    rw   rx   zhttps://zenodo.org/api/records/c                 S      i | ]}|d  |qS )keyr   .0itemr   r   r   
<dictcomp>w      z'zenodo_download_url.<locals>.<dictcomp>filesFile '' not found in data archive  (doi:).linksr!   )r   r1   r2   jsonr   )ry   rz   r	   
article_idarticler   r   r   r   r   r|   a  s   r|   c                 C   s   t d|  d }|d }t d| d}|  dd | D }||vr8td| d	|  d
| d|| d }|S )r   z)https://api.figshare.com/v2/articles?doi=r   idz%https://api.figshare.com/v2/articles/z/filesc                 S   r   )namer   r   r   r   r   r     r   z)figshare_download_url.<locals>.<dictcomp>r   r   r   r   r   )r1   r2   r   r3   r   )ry   rz   r	   r   r   rA   r   r   r   r   r   r{     s   r{   )F)rL   r8   r`   r1   utilsr   r   ImportErrorrk   r   r   r   r   r   r}   r|   r{   r   r   r   r   <module>   s2   
7 oo 