U ��.e_��@sDddlZddlmZddlZddlZddlZddlZddlZz ddlZWne k rdddl ZYnXddl Z ddl m Z ddlmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZddlm Z m!Z!m"Z"ddl#m$Z$m%Z%ddl&m'Z'm(Z(m)Z)m*Z*m+Z+m,Z,m-Z-m.Z.m/Z/dd l0m1Z1m2Z2dd l3m4Z4m5Z5e�6e7�Z8e�9d �Z:e�9d ej;�Zd-dd�Z?Gdd�de�Z@Gdd�deA�ZBGdd�deB�ZCGdd�deB�ZDGdd�deA�ZEGdd�deB�ZFGdd�deB�ZGGdd �d eB�ZHGd!d"�d"eB�ZIGd#d$�d$eB�ZJeJeH�eFd%d&d'�d(d)�ZKeKjLZLe�9d*�ZMGd+d,�d,eA�ZNdS).�N)�BytesIO�)�DistlibException)�urljoin�urlparse� urlunparse� url2pathname� pathname2url�queue�quote�unescape� string_types� build_opener�HTTPRedirectHandler� text_type�Request� HTTPError�URLError)� Distribution�DistributionPath� make_dist)�Metadata�MetadataInvalidError) �cached_property�parse_credentials� ensure_slash�split_filename�get_project_data�parse_requirement�parse_name_and_version� ServerProxy�normalize_name)� get_scheme�UnsupportedVersionError)�Wheel� is_compatiblez^(\w+)=([a-f0-9]+)z;\s*charset\s*=\s*(.*)\s*$ztext/html|application/x(ht)?mlzhttps://pypi.org/pypicCs6|dkr t}t|dd�}z |��W�S|d��XdS)z� Return all distribution names known by an index. :param url: The URL of the index. :return: A list of all known distribution names. N�@��timeout�close)� DEFAULT_INDEXr � list_packages)�url�client�r.�@/usr/lib/python3.8/site-packages/pip/_vendor/distlib/locators.py�get_all_distribution_names)s   r0c@s$eZdZdZdd�ZeZZZdS)�RedirectHandlerzE A class to work around a bug in some Python 3.2.x releases. c Cs�d}dD]}||kr||}q"q|dkr.dSt|�}|jdkrnt|��|�}t|d�rf|�||�n|||<t�||||||�S)N)�locationZuri��replace_header)r�schemerZ get_full_url�hasattrr4�BaseRedirectHandler�http_error_302) �self�req�fp�code�msg�headersZnewurl�keyZurlpartsr.r.r/r8@s   �zRedirectHandler.http_error_302N)�__name__� __module__� __qualname__�__doc__r8Zhttp_error_301Zhttp_error_303Zhttp_error_307r.r.r.r/r17sr1c@s�eZdZdZdZdZdZdZedZd)dd �Z d d �Z d d �Z dd�Z dd�Z dd�Zee e�Zdd�Zdd�Zdd�Zdd�Zdd�Zdd�Zd d!�Zd"d#�Zd$d%�Zd*d'd(�ZdS)+�LocatorzG A base class for locators - things that locate distributions. )z.tar.gzz.tar.bz2z.tarz.zipz.tgzz.tbz)z.eggz.exe�.whl)z.pdfN)rE�defaultcCs,i|_||_tt��|_d|_t��|_dS)a^ Initialise an instance. :param scheme: Because locators look for most recent versions, they need to know the version scheme to use. This specifies the current PEP-recommended scheme - use ``'legacy'`` if you need to support existing distributions on PyPI. N) �_cacher5rr1�opener�matcherr �Queue�errors)r9r5r.r.r/�__init__fs  zLocator.__init__cCsVg}|j��sRz|j�d�}|�|�Wn|jjk rDYqYnX|j��q|S)z8 Return any errors which have occurred. F)rK�empty�get�appendZEmpty� task_done)r9�result�er.r.r/� get_errorsys    zLocator.get_errorscCs |��dS)z> Clear any errors which may have been logged. N)rS�r9r.r.r/� clear_errors�szLocator.clear_errorscCs|j��dS�N)rG�clearrTr.r.r/� clear_cache�szLocator.clear_cachecCs|jSrV��_schemerTr.r.r/� _get_scheme�szLocator._get_schemecCs ||_dSrVrY)r9�valuer.r.r/� _set_scheme�szLocator._set_schemecCs td��dS)a= For a given project, get a dictionary mapping available versions to Distribution instances. This should be implemented in subclasses. If called from a locate() request, self.matcher will be set to a matcher for the requirement to satisfy, otherwise it will be None. � Please implement in the subclassN��NotImplementedError)r9�namer.r.r/� _get_project�s zLocator._get_projectcCs td��dS)�J Return all the distribution names known to this locator. r^Nr_rTr.r.r/�get_distribution_names�szLocator.get_distribution_namescCsL|jdkr|�|�}n2||jkr,|j|}n|��|�|�}||j|<|S)z� For a given project, get a dictionary mapping available versions to Distribution instances. This calls _get_project to do all the work, and just implements a caching layer on top. N)rGrbrU)r9rarQr.r.r/� get_project�s      zLocator.get_projectcCs^t|�}t�|j�}d}|�d�}|�|j�}|rBtt|�|j�}|j dkd|j k||||fS)zu Give an url a score which can be used to choose preferred URLs for a given project release. TrE�httpszpypi.org) r� posixpath�basename�path�endswith�downloadable_extensionsr%r$� wheel_tagsr5�netloc)r9r,�trhZ compatibleZis_wheelZis_downloadabler.r.r/� score_url�s   �zLocator.score_urlcCsR|}|rN|�|�}|�|�}||kr(|}||kr@t�d||�nt�d||�|S)a{ Choose one of two URLs where both are candidates for distribution archives for the same version of a distribution (for example, .tar.gz vs. zip). The current implementation favours https:// URLs over http://, archives from PyPI over those from other locations, wheel compatibility (if a wheel) and then the archive name. zNot replacing %r with %rzReplacing %r with %r)ro�logger�debug)r9�url1�url2rQ�s1�s2r.r.r/� prefer_url�s   zLocator.prefer_urlcCs t||�S)zZ Attempt to split a filename in project name, version and Python version. )r)r9�filename� project_namer.r.r/r�szLocator.split_filenamec Csdd�}d}t|�\}}}}} } | ���d�rz~t |�}t ||j �s�t�d |�nX|dkr�d }n ||j |�}|�r|j |j |jt||||| d f�d �dd�|jD��d�}Wn0tk �r:}zt�d|�W5d}~XYnXn�|�|j��sZt�d|�n�t�|�}}|jD]�}|�|��rn|dt|� �}|�||�}|�s�t�d|�nH|\}}}|�r�|||��r�|||t||||| d f�d�}|�r�||d<�q�qn|�r| �r| |d| <|S)a See if a URL is a candidate for a download URL for a project (the URL has typically been scraped from an HTML page). If it is, a dictionary is returned with keys "name", "version", "filename" and "url"; otherwise, None is returned. cSst|�t|�kSrV)r!)Zname1Zname2r.r.r/� same_project�sz:Locator.convert_url_to_download_info..same_projectNzegg=z %s: version hint in fragment: %r)NN������/rEzWheel not compatible: %sTr3z, cSs"g|]}d�t|dd����qS)�.�N)�join�list)�.0�vr.r.r/� sz8Locator.convert_url_to_download_info..)ra�versionrwr,�python-versionzinvalid path for wheel: %szNot downloadable: %sz No match for project/version: %s)rar�rwr,r�� %s_digest)r�lower� startswithrprq� HASHER_HASH�match�groupsrjr$r%rlrar�rwrr~�pyver� Exception�warningrkrgrh�lenr)r9r,rxryrQr5rmri�params�query�frag�m�algo�digestZorigpath�wheelZincluderRrwZextrnrar�r�r.r.r/�convert_url_to_download_info�s��     ���      ��   z$Locator.convert_url_to_download_infocCs2d}dD]$}d|}||kr|||f}q.q|S)z� Get a digest from a dictionary by looking at keys of the form 'algo_digest'. Returns a 2-tuple (algo, digest) if found, else None. Currently looks only for SHA256, then MD5. N)Zsha256�md5r�r.)r9�inforQr�r?r.r.r/� _get_digest1s zLocator._get_digestc Cs�|�d�}|�d�}||kr,||}|j}nt|||jd�}|j}|�|�|_}|d}||d|<|j|dkr�|�|j|�|_|d�|t ��� |�||_ |||<dS)z� Update a result dictionary (the final result from _get_project) with a dictionary for a specific version, which typically holds information gleaned from a filename or URL for an archive for the distribution. rar��r5r,�digests�urlsN) �pop�metadatarr5r�r�� source_urlrv� setdefault�set�add�locator) r9rQr�rar��dist�mdr�r,r.r.r/�_update_version_dataAs   zLocator._update_version_dataFc Cs�d}t|�}|dkr td|��t|j�}|�|j�|_}t�d|t|�j �|� |j �}t |�dk�r2g}|j } |D]z} | dkr�qxzH|�| �s�t�d|| �n*|s�| | �js�|�| �nt�d| |j �Wqxtk r�t�d|| �YqxXqxt |�d k�rt||jd �}|�r2t�d |�|d } || }|�r�|j�rH|j|_|�d i��| t��|_i} |�di�} |jD]}|| k�rv| || |<�qv| |_d|_|S)a Find the most recent distribution which matches the given requirement. :param requirement: A requirement of the form 'foo (1.0)' or perhaps 'foo (>= 1.0, < 2.0, != 1.3)' :param prereleases: If ``True``, allow pre-release versions to be located. Otherwise, pre-release versions are not returned. :return: A :class:`Distribution` instance, or ``None`` if no such distribution could be located. NzNot a valid requirement: %rzmatcher: %s (%s)r}�r�r�z%s did not match %rz%skipping pre-release version %s of %szerror matching %s with %rr)r?zsorted list: %srzr�r�)rrr"r5rI� requirementrprq�typer@rerar�Z version_classr�Z is_prereleaserOr�r��sortedr?ZextrasrNr�� download_urlsr�)r9r�� prereleasesrQ�rr5rI�versionsZslistZvcls�kr��dZsdr,r.r.r/�locateXsX      �    zLocator.locate)rF)F)r@rArBrC�source_extensions�binary_extensions�excluded_extensionsrlrkrLrSrUrXr[r]�propertyr5rbrdrerorvrr�r�r�r�r.r.r.r/rDVs.   JrDcs0eZdZdZ�fdd�Zdd�Zdd�Z�ZS)�PyPIRPCLocatorz� This locator uses XML-RPC to locate distributions. It therefore cannot be used with simple mirrors (that only mirror file content). c s*tt|�jf|�||_t|dd�|_dS)z� Initialise an instance. :param url: The URL to use for XML-RPC. :param kwargs: Passed to the superclass constructor. r&r'N)�superr�rL�base_urlr r-�r9r,�kwargs�� __class__r.r/rL�szPyPIRPCLocator.__init__cCst|j���S�rc)r�r-r+rTr.r.r/rd�sz%PyPIRPCLocator.get_distribution_namesc Csiid�}|j�|d�}|D]�}|j�||�}|j�||�}t|jd�}|d|_|d|_|�d�|_ |�dg�|_ |�d�|_ t |�}|r|d } | d |_ |�| �|_||_|||<|D]:} | d } |�| �} |d �|t���| �| |d | <q�q|S) Nr�Tr�rar��license�keywords�summaryrr,r�r�)r-Zpackage_releasesZ release_urlsZ release_datarr5rar�rNr�r�r�rr�r�r�r�r�r�r�) r9rarQr�r�r��datar�r�r�r,r�r.r.r/rb�s0         zPyPIRPCLocator._get_project�r@rArBrCrLrdrb� __classcell__r.r.r�r/r��s r�cs0eZdZdZ�fdd�Zdd�Zdd�Z�ZS)�PyPIJSONLocatorzw This locator uses PyPI's JSON interface. It's very limited in functionality and probably not worth using. c s tt|�jf|�t|�|_dSrV)r�r�rLrr�r�r�r.r/rL�szPyPIJSONLocator.__init__cCs td��dS�rczNot available from this locatorNr_rTr.r.r/rd�sz&PyPIJSONLocator.get_distribution_namesc Cs iid�}t|jdt|��}�z�|j�|�}|����}t�|�}t |j d�}|d}|d|_ |d|_ |� d�|_|� dg�|_|� d �|_t|�}||_|d } |||j <|d D]T} | d }|j�|�|�| �|j|<|d �|j t���|�|�| �|d |<q�|d ��D]�\} } | |j k�r4�qt |j d�} |j | _ | | _ t| �}||_||| <| D]T} | d }|j�|�|�| �|j|<|d �| t���|�|�| �|d |<�qh�qWn@tk �r}z |j�t|��t�d|�W5d}~XYnX|S)Nr�z%s/jsonr�r�rar�r�r�r�r�r,r�ZreleaseszJSON fetch failed: %s) rr�r rH�open�read�decode�json�loadsrr5rar�rNr�r�r�rr�r�r�r�r�r�r��itemsr�rK�putrrp� exception)r9rarQr,�respr�r�r�r�r�r�r�ZinfosZomd�odistrRr.r.r/rb�sT                zPyPIJSONLocator._get_projectr�r.r.r�r/r��s r�c@s`eZdZdZe�dejejBejB�Z e�dejejB�Z dd�Z e�dej�Z e dd��Zd S) �Pagez4 This class represents a scraped HTML page. z� (rel\s*=\s*(?:"(?P[^"]*)"|'(?P[^']*)'|(?P[^>\s ]*))\s+)? href\s*=\s*(?:"(?P[^"]*)"|'(?P[^']*)'|(?P[^>\s ]*)) (\s+rel\s*=\s*(?:"(?P[^"]*)"|'(?P[^']*)'|(?P[^>\s ]*)))? z!]+)cCs4||_||_|_|j�|j�}|r0|�d�|_dS)zk Initialise an instance with the Unicode page contents and the URL they came from. rN)r�r�r,�_base�search�group)r9r�r,r�r.r.r/rLs  z Page.__init__z[^a-z0-9$&+,/:;=?@.#%_\\|-]cCs�dd�}t�}|j�|j�D]�}|�d�}|dpX|dpX|dpX|dpX|dpX|d }|d pp|d pp|d }t|j|�}t|�}|j� d d�|�}|� ||f�qt |dd�dd�}|S)z� Return the URLs of all the links on a page together with information about their "rel" attribute, for determining which ones to treat as downloads and which ones to queue for further scraping. cSs,t|�\}}}}}}t||t|�|||f�S)zTidy up an URL.)rrr )r,r5rmrir�r�r�r.r.r/�clean-s  �zPage.links..cleanr3Zrel1Zrel2Zrel3Zrel4Zrel5Zrel6rrrsZurl3cSsdt|�d��S)Nz%%%2xr)�ordr�)r�r.r.r/�;�zPage.links..cSs|dS)Nrr.)rnr.r.r/r�?r�T)r?�reverse) r��_href�finditerr�� groupdictrr�r � _clean_re�subr�r�)r9r�rQr�r��relr,r.r.r/�links&s$ ��� z Page.linksN)r@rArBrC�re�compile�I�S�Xr�r�rLr�rr�r.r.r.r/r� s� r�cs�eZdZdZejdd�dd�d�Zd�fdd � Zd d �Zd d �Z dd�Z e � de j �Zdd�Zdd�Zdd�Zdd�Zdd�Ze � d�Zdd�Z�ZS)�SimpleScrapingLocatorz� A locator which scrapes HTML pages to locate downloads for a distribution. This runs multiple threads to do the I/O; performance is at least as good as pip's PackageFinder, which works in an analogous fashion. cCstjtt�d���S)N)Zfileobj)�gzipZGzipFilerr�r���br.r.r/r�Mr�zSimpleScrapingLocator.cCs|SrVr.r�r.r.r/r�Nr�)Zdeflater�ZnoneN� c sltt|�jf|�t|�|_||_i|_t�|_t � �|_ t�|_ d|_ ||_t��|_t��|_d|_dS)a� Initialise an instance. :param url: The root URL to use for scraping. :param timeout: The timeout, in seconds, to be applied to requests. This defaults to ``None`` (no timeout specified). :param num_workers: The number of worker threads you want to do I/O, This defaults to 10. :param kwargs: Passed to the superclass. FN)r�r�rLrr�r(� _page_cacher��_seenr rJ� _to_fetch� _bad_hosts�skip_externals� num_workers� threading�RLock�_lock�_gplock�platform_check)r9r,r(r�r�r�r.r/rLQs     zSimpleScrapingLocator.__init__cCsFg|_t|j�D]0}tj|jd�}|�d�|��|j�|�qdS)z� Threads are created only when get_project is called, and terminate before it returns. They are there primarily to parallelise I/O (i.e. fetching web pages). )�targetTN) �_threads�ranger�r�ZThread�_fetchZ setDaemon�startrO)r9�irnr.r.r/�_prepare_threadsls  z&SimpleScrapingLocator._prepare_threadscCs6|jD]}|j�d�q|jD] }|��qg|_dS)zu Tell all the threads to terminate (by sending a sentinel value) and wait for them to do so. N)r�r�r�r~)r9rnr.r.r/� _wait_threadsys    z#SimpleScrapingLocator._wait_threadsc Cs�iid�}|j�x||_||_t|jdt|��}|j��|j��|� �z&t � d|�|j �|�|j ��W5|� �X|`W5QRX|S)Nr�z%s/z Queueing %s)r�rQrxrr�r r�rWr�r�r�rprqr�r�r~)r9rarQr,r.r.r/rb�s      z"SimpleScrapingLocator._get_projectz<\b(linux_(i\d86|x86_64|arm\w+)|win(32|_amd64)|macosx_?\d+)\bcCs |j�|�S)zD Does an URL refer to a platform-specific download? )�platform_dependentr�)r9r,r.r.r/�_is_platform_dependent�sz,SimpleScrapingLocator._is_platform_dependentc CsZ|jr|�|�rd}n|�||j�}t�d||�|rV|j�|�|j|�W5QRX|S)a% See if an URL is a suitable download for a project. If it is, register information in the result dictionary (for _get_project) about the specific version it's for. Note that the return value isn't actually used other than as a boolean value. Nzprocess_download: %s -> %s) r�r�r�rxrprqr�r�rQ)r9r,r�r.r.r/�_process_download�s z'SimpleScrapingLocator._process_downloadc Cs�t|�\}}}}}}|�|j|j|j�r2d}n||jrJ|�|j�sJd}nd|�|j�s\d}nR|dkrjd}nD|dkrxd}n6|�|�r�d}n&|� dd�d} | � �dkr�d}nd}t � d ||||�|S) z� Determine whether a link URL from a referring page and with a particular "rel" attribute should be queued for scraping. F)ZhomepageZdownload)ZhttprfZftp�:rrZ localhostTz#should_queue: %s (%s) from %s -> %s) rrjr�r�r�r�r�r�r��splitr�rprq) r9�linkZreferrerr�r5rmri�_rQ�hostr.r.r/� _should_queue�s0�    �z#SimpleScrapingLocator._should_queuec Cs�|j��}z�z�|r�|�|�}|dkr,WW��q|jD]j\}}||jkr2zB|j�|�|�|�s�|�|||�r�t � d||�|j� |�Wq2t k r�Yq2Xq2Wn2t k r�}z|j� t|��W5d}~XYnXW5|j��X|sq�qdS)z� Get a URL to fetch from the work queue, get the HTML page, examine its links for download candidates and candidates for further scraping. This is a handy method to run in a thread. NzQueueing %s from %s)r�rNrP�get_pager�r�r�r�rrprqr�rr�rKr)r9r,�pagerr�rRr.r.r/r��s,      � & zSimpleScrapingLocator._fetchc CsXt|�\}}}}}}|dkr:tj�t|��r:tt|�d�}||jkr`|j|}t� d||��n�|� dd�d}d}||j kr�t� d||��n�t |d d id �}�z�z�t� d |�|j j||jd �} t� d|�| ��} | �dd�} t�| ��r�| ��} | ��} | �d�}|�r"|j|}|| �} d}t�| �}|�r@|�d�}z| �|�} Wn tk �rn| �d�} YnXt| | �}||j| <Wn�tk �r�}z|jdk�r�t�d||�W5d}~XYn�t k �r}z0t�d||�|j!�|j �"|�W5QRXW5d}~XYn2t#k �rB}zt�d||�W5d}~XYnXW5||j|<X|S)a Get the HTML for an URL, possibly from an in-memory cache. XXX TODO Note: this cache is never actually cleared. It's assumed that the data won't get stale over the lifetime of a locator instance (not necessarily true for the default_locator). �filez index.htmlzReturning %s from cache: %srrrNzSkipping %s due to bad host %szAccept-encodingZidentity)r>z Fetching %sr'z Fetched %sz Content-Typer3zContent-Encodingzutf-8zlatin-1i�zFetch failed: %s: %s)$r�osri�isdirrrrr�rprqrr�rrHr�r(r�rN�HTML_CONTENT_TYPEr�Zgeturlr��decoders�CHARSETr�r�r�� UnicodeErrorr�rr<r�rr�r�r�)r9r,r5rmrirrQrr:r�r>Z content_typeZ final_urlr��encoding�decoderr�rRr.r.r/r�sZ              &$ zSimpleScrapingLocator.get_pagez]*>([^<]+)[\w-]+)\s*\(\s*(==\s*)?(?P[^)]+)\)$c@sLeZdZdZddd�Zdd�Zdd�Zd d �Zd d �Zd d�Z ddd�Z dS)�DependencyFinderz0 Locate dependencies for distributions. NcCs|pt|_t|jj�|_dS)zf Initialise an instance, using the specified locator to locate distributions. N)�default_locatorr�r"r5r'r.r.r/rL*s zDependencyFinder.__init__cCsrt�d|�|j}||j|<||j||jf<|jD]:}t|�\}}t�d|||�|j� |t ��� ||f�q2dS)z� Add a distribution to the finder. This will update internal information about who provides what. :param dist: The distribution to add. zadding distribution %szAdd to provided: %s, %s, %sN) rprqr?� dists_by_name�distsr��providesr�providedr�r�r�)r9r�ra�pr�r.r.r/�add_distribution2s    z!DependencyFinder.add_distributioncCsxt�d|�|j}|j|=|j||jf=|jD]D}t|�\}}t�d|||�|j|}|� ||f�|s.|j|=q.dS)z� Remove a distribution from the finder. This will update internal information about who provides what. :param dist: The distribution to remove. zremoving distribution %sz Remove from provided: %s, %s, %sN) rprqr?r.r/r�r0rr1�remove)r9r�rar2r��sr.r.r/�remove_distributionAs    z$DependencyFinder.remove_distributioncCsBz|j�|�}Wn,tk r<|��d}|j�|�}YnX|S)z� Get a version matcher for a requirement. :param reqt: The requirement :type reqt: str :return: A version matcher (an instance of :class:`distlib.version.Matcher`). r)r5rIr#r)r9�reqtrIrar.r.r/� get_matcherSs  zDependencyFinder.get_matcherc Cst|�|�}|j}t�}|j}||krp||D]B\}}z|�|�}Wntk rZd}YnX|r,|�|�qpq,|S)z� Find the distributions which can fulfill a requirement. :param reqt: The requirement. :type reqt: str :return: A set of distribution which can fulfill the requirement. F)r8r?r�r1r�r#r�) r9r7rIrarQr1r��providerr�r.r.r/�find_providerscs   zDependencyFinder.find_providersc Cs�|j|}t�}|D]$}|�|�}|�|j�s|�|�q|rZ|�d||t|�f�d}n@|�|�|j|=|D]}|j�|t���|�qp|� |�d}|S)a� Attempt to replace one provider with another. This is typically used when resolving dependencies from multiple sources, e.g. A requires (B >= 1.0) while C requires (B >= 1.1). For successful replacement, ``provider`` must meet all the requirements which ``other`` fulfills. :param provider: The provider we are trying to replace with. :param other: The provider we're trying to replace. :param problems: If False is returned, this will contain what problems prevented replacement. This is currently a tuple of the literal string 'cantreplace', ``provider``, ``other`` and the set of requirements that ``provider`` couldn't fulfill. :return: True if we can replace ``other`` with ``provider``, else False. Z cantreplaceFT) �reqtsr�r8r�r�r�� frozensetr6r�r3) r9r9�other�problemsZrlistZ unmatchedr5rIrQr.r.r/�try_to_replace{s$     �  zDependencyFinder.try_to_replaceFcCs�i|_i|_i|_i|_t|p g�}d|krH|�d�|tdddg�O}t|t�rh|}}t� d|�n4|j j ||d�}}|dkr�t d|��t� d |�d |_ t�}t|g�}t|g�}|�r�|��}|j} | |jkr�|�|�n"|j| } | |k�r|�|| |�|j|jB} |j} t�} |�r`||k�r`d D]*}d |}||k�r4| t|d |�O} �q4| | B| B}|D�].}|�|�}|�sFt� d|�|j j ||d�}|dk�r�|�s�|j j |d d�}|dk�r�t� d|�|�d|f�n^|j|j}}||f|jk�r|�|�|�|�|| k�rF||k�rF|�|�t� d|j�|D]R}|j} | |jk�rx|j�|t���|�n"|j| } | |k�rJ|�|| |��qJ�qpq�t|j���}|D]&}||k|_|j�r�t� d|j��q�t� d|�||fS)a� Find a distribution and all distributions it depends on. :param requirement: The requirement specifying the distribution to find, or a Distribution instance. :param meta_extras: A list of meta extras such as :test:, :build: and so on. :param prereleases: If ``True``, allow pre-release versions to be returned - otherwise, don't return prereleases unless they're all that's available. Return a set of :class:`Distribution` instances and a set of problems. The distributions returned should be such that they have the :attr:`required` attribute set to ``True`` if they were from the ``requirement`` passed to ``find()``, and they have the :attr:`build_time_dependency` attribute set to ``True`` unless they are post-installation dependencies of the ``requirement``. The problems should be a tuple consisting of the string ``'unsatisfied'`` and the requirement which couldn't be satisfied by any distribution known to the locator. z:*:z:test:z:build:z:dev:zpassed %s as requirement)r�NzUnable to locate %rz located %sT)ZtestZbuildZdevz:%s:z %s_requireszNo providers found for %rzCannot satisfy %rZ unsatisfiedzAdding %s to install_distsz#%s is a build-time dependency only.zfind done for %s)r1r/r.r;r�r4� isinstancerrprqr�r�rZ requestedr�r?r3r?Z run_requiresZ meta_requiresZbuild_requires�getattrr:r�r�Zname_and_versionr��valuesZbuild_time_dependency)r9r�Z meta_extrasr�r�r�r>ZtodoZ install_distsrar=ZireqtsZsreqtsZereqtsr?rRZ all_reqtsr�Z providersr9�nr�r2r/r.r.r/�find�s�   �                    �    � zDependencyFinder.find)N)NF) r@rArBrCrLr3r6r8r:r?rDr.r.r.r/r,%s (r,)N)Or��iorr�Zloggingr rgr�r�� ImportErrorZdummy_threadingrr3r�compatrrrrr r r r r rrr7rrrrZdatabaserrrr�rr�utilrrrrrrrr r!r�r"r#r�r$r%Z getLoggerr@rpr�r�r�r r r*r0r1�objectrDr�r�r�r�rrr"r$r-r�ZNAME_VERSION_REr,r.r.r.r/�s^   D,    @0E:zA&[��