U
    7cE                     @   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mZ ddlm	Z	m
Z
 ddlmZ ddl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 ddlmZ ddlmZ ddlmZm Z  ddl!m"Z"m#Z# er@ddl$m%Z%m&Z&m'Z'm(Z(m)Z)m*Z*m+Z+m,Z, ddl-Z.ddl/m0Z0 ddl1m2Z2 ddl3m4Z4 e.j5j6j7Z8e(e9e9f Z:e;e<Z=dd Z>dd Z?G dd de@ZAdd ZBG dd de@ZCdd ZDdd  ZEd!d" ZFd#d$ ZGd%d& ZHd'd( ZId)d* ZJG d+d, d,eKZLd<d-d.ZMd/d0 ZNd=d1d2ZOd3d4 ZPd>d6d7ZQG d8d9 d9eKZRG d:d; d;eKZSdS )?zM
The main purpose of this module is to expose LinkCollector.collect_links().
    N)OrderedDict)html5librequests)unescape)	HTTPError
RetryErrorSSLError)parse)requestLink)ARCHIVE_EXTENSIONS)redact_auth_from_url)MYPY_CHECK_RUNNING)path_to_urlurl_to_path)is_urlvcs)CallableIterableListMutableMappingOptionalSequenceTupleUnion)Response)SearchScope)
PipSessionc                 C   s6   t jD ]*}|  |r| t| dkr|  S qdS )zgLook for VCS schemes in the URL.

    Returns the matched VCS scheme, or None if there's no match.
    z+:N)r   schemeslower
startswithlen)urlscheme r%   A/tmp/pip-unpacked-wheel-xvghy_sv/pip/_internal/index/collector.py_match_vcs_scheme,   s    

r'   c                 C   s(   t | j}tD ]}||r dS qdS )z2Return whether the URL looks like an archive.
    TF)r   filenamer   endswith)r#   r(   bad_extr%   r%   r&   _is_url_like_archive8   s
    

r+   c                       s   e Zd Z fddZ  ZS )_NotHTMLc                    s"   t t| || || _|| _d S N)superr,   __init__content_typerequest_desc)selfr0   r1   	__class__r%   r&   r/   D   s    z_NotHTML.__init__)__name__
__module____qualname__r/   __classcell__r%   r%   r3   r&   r,   C   s   r,   c                 C   s.   | j dd}| ds*t|| jjdS )zCheck the Content-Type header to ensure the response contains HTML.

    Raises `_NotHTML` if the content type is not text/html.
    Content-Type 	text/htmlN)headersgetr    r!   r,   r
   method)responser0   r%   r%   r&   _ensure_html_headerK   s    r@   c                   @   s   e Zd ZdS )_NotHTTPN)r5   r6   r7   r%   r%   r%   r&   rA   V   s   rA   c                 C   sD   t | \}}}}}|dkr"t |j| dd}|  t| dS )zSend a HEAD request to the URL, and ensure the response contains HTML.

    Raises `_NotHTTP` if the URL is not available for a HEAD request, or
    `_NotHTML` if the content type is not text/html.
    >   httphttpsT)allow_redirectsN)urllib_parseurlsplitrA   headraise_for_statusr@   )r#   sessionr$   netlocpathqueryfragmentrespr%   r%   r&   _ensure_html_responseZ   s    rO   c                 C   sL   t | rt| |d tdt|  |j| dddd}|  t| |S )a  Access an HTML page with GET, and return the response.

    This consists of three parts:

    1. If the URL looks suspiciously like an archive, send a HEAD first to
       check the Content-Type is HTML, to avoid downloading a large file.
       Raise `_NotHTTP` if the content type cannot be determined, or
       `_NotHTML` if it is not HTML.
    2. Actually perform the request. Raise HTTP exceptions on network failures.
    3. Check the Content-Type header to make sure we got HTML, and raise
       `_NotHTML` otherwise.
    rI   zGetting page %sr;   z	max-age=0)AcceptzCache-Control)r<   )r+   rO   loggerdebugr   r=   rH   r@   )r#   rI   rN   r%   r%   r&   _get_html_responsek   s    rT   c                 C   s2   | r.d| kr.t | d \}}d|kr.|d S dS )zBDetermine if we have any encoding information in our headers.
    r9   charsetN)cgiparse_header)r<   r0   paramsr%   r%   r&   _get_encoding_from_headers   s
    rY   c                 C   s.   |  dD ]}|d}|dk	r
|  S q
|S )a  Determine the HTML document's base URL.

    This looks for a ``<base>`` tag in the HTML document. If present, its href
    attribute denotes the base URL of anchor tags in the document. If there is
    no such tag (or if it does not have a valid href attribute), the HTML
    file's URL is used as the base URL.

    :param document: An HTML document representation. The current
        implementation expects the result of ``html5lib.parse()``.
    :param page_url: The URL of the HTML document.
    z.//basehrefN)findallr=   )documentpage_urlbaserZ   r%   r%   r&   _determine_base_url   s
    

r_   c                 C   sP   t | }|jdkr(tt|j}nt jt |jdd}t 	|j
|dS )zMakes sure a link is fully encoded.  That is, if a ' ' shows up in
    the link, it will be rewritten to %20 (while not over-quoting
    % or other characters).r:   z/@)safe)rK   )rE   urlparserJ   urllib_requestpathname2urlurl2pathnamerK   quoteunquote
urlunparse_replace)r#   resultrK   r%   r%   r&   _clean_link   s    	


rj   c                 C   sf   |  d}|sdS tt||}|  d}|r8t|nd}|  d}|rRt|}t||||d}|S )zJ
    Convert an anchor element in a simple repository page to a Link.
    rZ   Nzdata-requires-pythonzdata-yanked)
comes_fromrequires_pythonyanked_reason)r=   rj   rE   urljoinr   r   )anchorr]   base_urlrZ   r#   	pyrequirerm   linkr%   r%   r&   _create_link_from_element   s     	


rs   c                 c   sV   t j| j| jdd}| j}t||}|dD ]"}t|||d}|dkrJq.|V  q.dS )zP
    Parse an HTML document, and yield its anchor elements as Link objects.
    F)transport_encodingnamespaceHTMLElementsz.//a)r]   rp   N)r   r	   contentencodingr#   r_   r[   rs   )pager\   r#   rp   ro   rr   r%   r%   r&   parse_links   s     
ry   c                   @   s    e Zd ZdZdd Zdd ZdS )HTMLPagez'Represents one page, along with its URLc                 C   s   || _ || _|| _dS )z
        :param encoding: the encoding to decode the given content.
        :param url: the URL from which the HTML was downloaded.
        N)rv   rw   r#   )r2   rv   rw   r#   r%   r%   r&   r/     s    zHTMLPage.__init__c                 C   s
   t | jS r-   )r   r#   r2   r%   r%   r&   __str__!  s    zHTMLPage.__str__N)r5   r6   r7   __doc__r/   r|   r%   r%   r%   r&   rz     s   rz   c                 C   s   |d krt j}|d| | d S )Nz%Could not fetch URL %s: %s - skipping)rR   rS   )rr   reasonmethr%   r%   r&   _handle_get_page_fail&  s    r   c                 C   s   t | j}t| j|| jdS )N)rw   r#   )rY   r<   rz   rv   r#   )r?   rw   r%   r%   r&   _make_html_page1  s    
r   c           
   
   C   s  |d krt d| jddd }t|}|r@td||  d S t|\}}}}}}|dkrtj	
t|r|ds|d7 }t|d}td	| zt||d
}W nD tk
r   td|  Y n, tk
r } ztd| |j|j W 5 d }~X Y n tk
r0 } zt| | W 5 d }~X Y n tk
r\ } zt| | W 5 d }~X Y n tk
r } z$d}	|	t|7 }	t| |	tjd W 5 d }~X Y n\ tjk
r } zt| d|  W 5 d }~X Y n* tjk
r   t| d Y n
X t|S d S )Nz?_get_html_page() missing 1 required keyword argument: 'session'#   r   zCannot look at %s URL %sfile/z
index.htmlz# file: URL is directory, getting %srP   zQSkipping page %s because it looks like an archive, and cannot be checked by HEAD.z<Skipping page %s because the %s request got Content-Type: %sz4There was a problem confirming the ssl certificate: )r   zconnection error: %sz	timed out)	TypeErrorr#   splitr'   rR   rS   rE   ra   osrK   isdirrb   rd   r)   rn   rT   rA   r,   r1   r0   r   r   r   r   strinfor   ConnectionErrorTimeoutr   )
rr   rI   r#   
vcs_schemer$   _rK   rN   excr~   r%   r%   r&   _get_html_page7  sV    

   r   c                 C   s   t t| S )zQ
    Return a list of links, with duplicates removed and ordering preserved.
    )listr   fromkeys)linksr%   r%   r&   _remove_duplicate_linksm  s    r   Fc                    s   g  g  fdd}| D ]}t j|}|d}|s<|r|rF|}nt|}t j|r|rt j|}t |D ]}|t j|| qtq|r	| qt
d| qt j|r|| qt
d| qt|r	| qt
d| q fS )z
    Divide a list of locations into two groups: "files" (archives) and "urls."

    :return: A pair of lists (files, urls).
    c                    s8   t | }tj|ddd dkr*| n
 | d S )NF)strictr   r;   )r   	mimetypes
guess_typeappend)rK   r#   filesurlsr%   r&   	sort_path  s    z"group_locations.<locals>.sort_pathzfile:z)Path '{0}' is ignored: it is a directory.z:Url '%s' is ignored: it is neither a file nor a directory.zQUrl '%s' is ignored. It is either a non-existing path or lacks a specific scheme.)r   rK   existsr!   r   r   realpathlistdirjoinr   rR   warningformatisfiler   )	locations
expand_dirr   r#   is_local_pathis_file_urlrK   itemr%   r   r&   group_locationsv  sF    

r   c                   @   s   e Zd ZdZdd ZdS )CollectedLinksa  
    Encapsulates the return value of a call to LinkCollector.collect_links().

    The return value includes both URLs to project pages containing package
    links, as well as individual package Link objects collected from other
    sources.

    This info is stored separately as:

    (1) links from the configured file locations,
    (2) links from the configured find_links, and
    (3) urls to HTML project pages, as described by the PEP 503 simple
        repository API.
    c                 C   s   || _ || _|| _dS )z
        :param files: Links from file locations.
        :param find_links: Links from find_links.
        :param project_urls: URLs to HTML project pages, as described by
            the PEP 503 simple repository API.
        Nr   
find_linksproject_urls)r2   r   r   r   r%   r%   r&   r/     s    zCollectedLinks.__init__N)r5   r6   r7   r}   r/   r%   r%   r%   r&   r     s   r   c                   @   s4   e Zd ZdZdd Zedd Zdd Zdd	 Zd
S )LinkCollectorz
    Responsible for collecting Link objects from all configured locations,
    making network requests as needed.

    The class's main method is its collect_links() method.
    c                 C   s   || _ || _d S r-   )search_scoperI   )r2   rI   r   r%   r%   r&   r/     s    zLinkCollector.__init__c                 C   s   | j jS r-   )r   r   r{   r%   r%   r&   r     s    zLinkCollector.find_linksc                 C   s   t || jdS )z>
        Fetch an HTML page containing package links.
        rP   )r   rI   )r2   locationr%   r%   r&   
fetch_page  s    zLinkCollector.fetch_pagec                    s    j }||}t|\}}t jdd\}}dd t||D }dd  jD }	 fddtdd |D d	d |D D }
t|
}
d
t|
|g}|
D ]}|	d| qt
d| t||	|
dS )zFind all available links for the given project name.

        :return: All the Link objects (unfiltered), as a CollectedLinks object.
        T)r   c                 S   s   g | ]}t |qS r%   r   .0r#   r%   r%   r&   
<listcomp>   s    z/LinkCollector.collect_links.<locals>.<listcomp>c                 S   s   g | ]}t |d qS )z-fr   r   r%   r%   r&   r     s     c                    s   g | ]} j |r|qS r%   )rI   is_secure_origin)r   rr   r{   r%   r&   r   
  s   c                 s   s   | ]}t |V  qd S r-   r   r   r%   r%   r&   	<genexpr>  s     z.LinkCollector.collect_links.<locals>.<genexpr>c                 s   s   | ]}t |V  qd S r-   r   r   r%   r%   r&   r     s     z,{} location(s) to search for versions of {}:z* {}
r   )r   get_index_urls_locationsr   r   	itertoolschainr   r   r"   r   rR   rS   r   r   )r2   project_namer   index_locationsindex_file_locindex_url_locfl_file_loc
fl_url_loc
file_linksfind_link_linksurl_locationslinesrr   r%   r{   r&   collect_links  s>    
 


 zLinkCollector.collect_linksN)	r5   r6   r7   r}   r/   propertyr   r   r   r%   r%   r%   r&   r     s   	
r   )N)N)F)Tr}   rV   r   loggingr   r   collectionsr   pip._vendorr   r   pip._vendor.distlib.compatr   Zpip._vendor.requests.exceptionsr   r   r   Zpip._vendor.six.moves.urllibr	   rE   r
   rb   pip._internal.models.linkr   pip._internal.utils.filetypesr   pip._internal.utils.miscr   pip._internal.utils.typingr   pip._internal.utils.urlsr   r   pip._internal.vcsr   r   typingr   r   r   r   r   r   r   r   xml.etree.ElementTreexmlZpip._vendor.requestsr   !pip._internal.models.search_scoper   pip._internal.network.sessionr   etreeElementTreeElementZHTMLElementr   ZResponseHeaders	getLoggerr5   rR   r'   r+   	Exceptionr,   r@   rA   rO   rT   rY   r_   rj   rs   ry   objectrz   r   r   r   r   r   r   r   r%   r%   r%   r&   <module>   s^   (

3  

6	
<#