3 Bu_@sDddlZddlmZddlZddlZddlZddlZddlZy 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 l3m4Z4m5Z5ej6e7Z8ej9d Z:ej9d ej;Zd-ddZ?GdddeZ@GdddeAZBGdddeBZCGdddeBZDGdddeAZEGdddeBZFGdddeBZGGdd d eBZHGd!d"d"eBZIGd#d$d$eBZJeJeHeFd%d&d'd(d)ZKeKjLZLej9d*ZMGd+d,d,eAZNdS).N)BytesIO)DistlibException)urljoinurlparse urlunparse url2pathname pathname2urlqueuequoteunescape string_types build_openerHTTPRedirectHandler text_typeRequest HTTPErrorURLError) DistributionDistributionPath make_dist)MetadataMetadataInvalidError) cached_propertyparse_credentials ensure_slashsplit_filenameget_project_dataparse_requirementparse_name_and_version ServerProxynormalize_name) get_schemeUnsupportedVersionError)Wheel is_compatiblez^(\w+)=([a-f0-9]+)z;\s*charset\s*=\s*(.*)\s*$ztext/html|application/x(ht)?mlzhttps://pypi.org/pypic Cs2|dkr t}t|dd}z|jS|dXdS)z Return all distribution names known by an index. :param url: The URL of the index. :return: A list of all known distribution names. Ng@)timeoutclose) DEFAULT_INDEXr list_packages)urlclientr,/builddir/build/BUILDROOT/alt-python36-pip-20.2.4-1.el7.x86_64/opt/alt/python36/lib/python3.6/site-packages/pip/_vendor/distlib/locators.pyget_all_distribution_names)s  r.c@s$eZdZdZddZeZZZdS)RedirectHandlerzE A class to work around a bug in some Python 3.2.x releases. c Csd}xdD]}||kr ||}Pq W|dkr0dSt|}|jdkrpt|j|}t|drh|j||n|||<tj||||||S)Nlocationurireplace_header)r0r1)rschemer get_full_urlhasattrr3BaseRedirectHandlerhttp_error_302) selfreqfpcodemsgheadersnewurlkeyurlpartsr,r,r-r8@s   zRedirectHandler.http_error_302N)__name__ __module__ __qualname____doc__r8http_error_301http_error_303http_error_307r,r,r,r-r/7sr/c@seZdZdZd/Zd0Zd1Zd Zed2Zd3ddZ ddZ ddZ ddZ ddZ ddZee eZddZddZddZd d!Zd"d#Zd$d%Zd&d'Zd(d)Zd*d+Zd4d-d.Zd S)5LocatorzG A base class for locators - things that locate distributions. .tar.gz.tar.bz2.tar.zip.tgz.tbz.egg.exe.whl.pdfNdefaultcCs,i|_||_tt|_d|_tj|_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) _cacher4rr/openermatcherr Queueerrors)r9r4r,r,r-__init__fs  zLocator.__init__c CsXg}xN|jjsRy|jjd}|j|Wn|jjk rDwYnX|jjqW|S)z8 Return any errors which have occurred. F)rYemptygetappendEmpty task_done)r9resulter,r,r- get_errorsys  zLocator.get_errorscCs |jdS)z> Clear any errors which may have been logged. N)rb)r9r,r,r- clear_errorsszLocator.clear_errorscCs|jjdS)N)rUclear)r9r,r,r- clear_cacheszLocator.clear_cachecCs|jS)N)_scheme)r9r,r,r- _get_schemeszLocator._get_schemecCs ||_dS)N)rf)r9valuer,r,r- _set_schemeszLocator._set_schemecCs tddS)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. z Please implement in the subclassN)NotImplementedError)r9namer,r,r- _get_projects zLocator._get_projectcCs tddS)zJ Return all the distribution names known to this locator. z Please implement in the subclassN)rj)r9r,r,r-get_distribution_namesszLocator.get_distribution_namescCsL|jdkr|j|}n2||jkr,|j|}n|j|j|}||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)rUrlrc)r9rkr`r,r,r- get_projects      zLocator.get_projectcCs^t|}tj|j}d}|jd}|j|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. Tz.whlhttpszpypi.org) r posixpathbasenamepathendswithdownloadable_extensionsr%r$ wheel_tagsr4netloc)r9r*trq compatibleis_wheelZis_downloadabler,r,r- score_urls   zLocator.score_urlcCsR|}|rN|j|}|j|}||kr(|}||kr@tjd||ntjd|||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)rzloggerdebug)r9url1url2r`s1s2r,r,r- prefer_urls   zLocator.prefer_urlcCs t||S)zZ Attempt to split a filename in project name, version and Python version. )r)r9filename project_namer,r,r-rszLocator.split_filenamecCs dd}d}t|\}}}}} } | jjdry~t |}t ||j stjd |nX|dkrd }n ||j |}|r|j |j |jt||||| d fd jd d|jDd}Wn0tk r:}ztjd|WYdd}~XnXn|j|jsZtjd|ntj|}}x|jD]}|j|rp|dt| }|j||}|stjd|nJ|\}}}| s|||r|||t||||| d fd}|r||d<PqpW|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|kS)N)r!)Zname1Zname2r,r,r- same_projectsz:Locator.convert_url_to_download_info..same_projectNzegg=z %s: version hint in fragment: %rr/z.whlzWheel not compatible: %sTr2z, cSs"g|]}djt|ddqS).N)joinlist).0vr,r,r- sz8Locator.convert_url_to_download_info..)rkversionrr*zpython-versionzinvalid path for wheel: %szNot downloadable: %sz No match for project/version: %s)rkrrr*zpython-versionz %s_digest)NNr)rlower startswithr{r| HASHER_HASHmatchgroupsrsr$r%rurkrrrrpyver Exceptionwarningrtrprqlenr)r9r*rrr`r4rvrrparamsqueryfragmalgodigestZorigpathwheelincluderarextrwrkrrr,r,r-convert_url_to_download_infosj             z$Locator.convert_url_to_download_infocCsld}d|kr8|d}x"dD]}||kr|||f}PqW|shx*dD]"}d|}||krB|||f}PqBW|S)z Get a digest from a dictionary by looking at a "digests" dictionary or keys of the form 'algo_digest'. Returns a 2-tuple (algo, digest) if found, else None. Currently looks only for SHA256, then MD5. Ndigestssha256md5z %s_digest)rr)rrr,)r9infor`rrr@r,r,r- _get_digest1s    zLocator._get_digestc Cs|jd}|jd}||kr,||}|j}nt|||jd}|j}|j||_}|d}||d|<|j|dkr|j|j||_|dj|t j |||_ |||<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. rkr)r4r*rurlsN) popmetadatarr4rr source_urlr setdefaultsetaddlocator) r9r`rrkrdistmdrr*r,r,r-_update_version_dataHs   zLocator._update_version_dataFc Csd}t|}|dkr td|t|j}|j|j|_}tjd|t|j |j |j }t |dkr8g}|j } x|D]|} | d krqzyJ|j| stjd|| n,|s| | j r|j| ntjd| |j Wqztk rtjd || YqzXqzWt |d krt||jd }|r8tjd ||d} || }|r|jrN|j|_|jdij| t|_i} |jdi} x&|jD]}|| kr~| || |<q~W| |_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)rrrz%s did not match %rz%skipping pre-release version %s of %szerror matching %s with %rr)r@zsorted list: %s)rrr)rrr"r4rW requirementr{r|typerBrnrkrZ version_classr is_prereleaser]rrsortedr@extrasr\r download_urlsr)r9r prereleasesr`rr4rWversionsslistZvclskrdsdr*r,r,r-locate_sT            zLocator.locate)rJrKrLrMrNrO)rPrQrR)rS)rR)rT)F)rBrCrDrEsource_extensionsbinary_extensionsexcluded_extensionsrurtrZrbrcrergripropertyr4rlrmrnrzrrrrrrr,r,r,r-rIVs.   JrIcs0eZdZdZfddZddZddZZS)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. g@)r&N)superrrZbase_urlr r+)r9r*kwargs) __class__r,r-rZszPyPIRPCLocator.__init__cCst|jjS)zJ Return all the distribution names known to this locator. )rr+r))r9r,r,r-rmsz%PyPIRPCLocator.get_distribution_namesc Csiid}|jj|d}x|D]}|jj||}|jj||}t|jd}|d|_|d|_|jd|_ |jdg|_ |jd|_ t |}|r|d } | d |_ |j| |_||_|||<xB|D]:} | d } |j| } |d j|tj| | |d | <qWqW|S) N)rrT)r4rkrlicensekeywordssummaryrr*rr)r+Zpackage_releasesZ release_urlsZ release_datarr4rkrr\rrrrrrrrrrr) r9rkr`rrrdatarrrr*rr,r,r-rls0           zPyPIRPCLocator._get_project)rBrCrDrErZrmrl __classcell__r,r,)rr-rs rcs0eZdZdZfddZddZddZZS)PyPIJSONLocatorzw This locator uses PyPI's JSON interface. It's very limited in functionality and probably not worth using. c s tt|jf|t||_dS)N)rrrZrr)r9r*r)rr,r-rZszPyPIJSONLocator.__init__cCs tddS)zJ Return all the distribution names known to this locator. zNot available from this locatorN)rj)r9r,r,r-rmsz&PyPIJSONLocator.get_distribution_namescCsiid}t|jdt|}y|jj|}|jj}tj|}t |j d}|d}|d|_ |d|_ |j d|_|j dg|_|j d |_t|}||_|d } |||j <x`|d D]T} | d }|jj||j| |j|<|d j|j tj||j| |d |<qWx|d jD]\} } | |j kr:q"t |j d} |j | _ | | _ t| }||_||| <x\| D]T} | d }|jj||j| |j|<|d j| tj||j| |d |<qpWq"WWn@tk r}z"|jjt|tjd|WYdd}~XnX|S)N)rrz%s/json)r4rrkrrrrrr*rZreleaseszJSON fetch failed: %s) rrr rVopenreaddecodejsonloadsrr4rkrr\rrrrrrrrrrritemsrrYputrr{ exception)r9rkr`r*resprrrrrrrinfosZomdodistrar,r,r-rlsT               " zPyPIJSONLocator._get_project)rBrCrDrErZrmrlrr,r,)rr-rs rc@s`eZdZdZejdejejBejBZ ejdejejBZ ddZ ejdejZ e ddZd 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||_||_|_|jj|j}|r0|jd|_dS)zk Initialise an instance with the Unicode page contents and the URL they came from. rN)rrr*_basesearchgroup)r9rr*rr,r,r-rZ s  z Page.__init__z[^a-z0-9$&+,/:;=?@.#%_\\|-]cCsdd}t}x|jj|jD]}|jd}|dpZ|dpZ|dpZ|dpZ|dpZ|d }|d pr|d pr|d }t|j|}t|}|jj d d|}|j ||fqWt |dddd}|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||||fS)zTidy up an URL.)rrr )r*r4rvrrrrrr,r,r-clean4s zPage.links..cleanr2Zrel1Zrel2Zrel3Zrel4Zrel5Zrel6r}r~Zurl3cSsdt|jdS)Nz%%%2xr)ordr)rr,r,r-BszPage.links..cSs|dS)Nrr,)rwr,r,r-rFsT)r@reverse) r_hreffinditerr groupdictrrr _clean_resubrr)r9rr`rrrelr*r,r,r-links-s  z Page.linksN)rBrCrDrErecompileISXrrrZrrrr,r,r,r-rs rcseZdZdZejdddddZdfdd Zd d Zd d Z ddZ e j de j ZddZddZddZddZddZe j dZddZZS)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. cCstjttdjS)N)fileobj)gzipGzipFilerrr)br,r,r-rTszSimpleScrapingLocator.cCs|S)Nr,)rr,r,r-rUs)deflaternoneN c sltt|jf|t||_||_i|_t|_t j |_ t|_ d|_ ||_tj|_tj|_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)rrrZrrr& _page_cacher_seenr rX _to_fetch _bad_hostsskip_externals num_workers threadingRLock_lock_gplockplatform_check)r9r*r&rr)rr,r-rZXs     zSimpleScrapingLocator.__init__cCsJg|_x>t|jD]0}tj|jd}|jd|j|jj|qWdS)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) _threadsrangerrThread_fetch setDaemonstartr])r9irwr,r,r-_prepare_threadsss  z&SimpleScrapingLocator._prepare_threadscCs>x|jD]}|jjdqWx|jD] }|jq$Wg|_dS)zu Tell all the threads to terminate (by sending a sentinel value) and wait for them to do so. N)rrrr)r9rwr,r,r- _wait_threadss    z#SimpleScrapingLocator._wait_threadscCsiid}|jx||_||_t|jdt|}|jj|jj|j z&t j d||j j ||j jWd|jX|`WdQRX|S)N)rrz%s/z Queueing %s)rr`rrrr rrdrr r{r|rrrr )r9rkr`r*r,r,r-rls      z"SimpleScrapingLocator._get_projectz<\b(linux_(i\d86|x86_64|arm\w+)|win(32|_amd64)|macosx_?\d+)\bcCs |jj|S)zD Does an URL refer to a platform-specific download? )platform_dependentr)r9r*r,r,r-_is_platform_dependentsz,SimpleScrapingLocator._is_platform_dependentc CsZ|jr|j|rd}n|j||j}tjd|||rV|j|j|j|WdQRX|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) rr rrr{r|rrr`)r9r*rr,r,r-_process_downloads z'SimpleScrapingLocator._process_downloadc Cst|\}}}}}}|j|j|j|jr2d}n~|jrL|j|j rLd}nd|j|js^d}nR|d krld}nD|dkrzd}n6|j|rd}n&|j ddd } | j d krd}nd }t j d |||||S)z Determine whether a link URL from a referring page and with a particular "rel" attribute should be queued for scraping. Fhomepagedownloadhttproftp:rr localhostTz#should_queue: %s (%s) from %s -> %s)rr)rror) rrsrrrrrrr splitrr{r|) r9linkZreferrerrr4rvrr_r`hostr,r,r- _should_queues*     z#SimpleScrapingLocator._should_queuecCsx|jj}zy|r|j|}|dkr(wxv|jD]l\}}||jkr0yD|jj||j| r|j|||rtj d|||jj |Wq0t k rYq0Xq0WWn2t k r}z|j j t|WYdd}~XnXWd|jjX|sPqWdS)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)rr\get_pagerrrr rr{r|rrrrYrr_)r9r*pagerrrar,r,r-rs,     & zSimpleScrapingLocator._fetchcCsXt|\}}}}}}|dkr:tjjt|r:tt|d}||jkr`|j|}tj d||n|j ddd}d}||j krtj d||nt |d d id }zytj d ||j j||jd } tj d|| j} | jdd} tj| r| j} | j} | jd}|r"|j|}|| } d}tj| }|r@|jd}y| j|} Wn tk rn| jd} YnXt| | }||j| <Wntk r}z |jdkrtjd||WYdd}~Xnt k r}z2tjd|||j!|j j"|WdQRXWYdd}~Xn2t#k rB}ztjd||WYdd}~XnXWd||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-encodingidentity)r>z Fetching %s)r&z Fetched %sz Content-Typer2zContent-Encodingzutf-8zlatin-1izFetch failed: %s: %s)$rosrrisdirrrrrr{r|rrrrVrr&rr\HTML_CONTENT_TYPErgeturlrdecodersCHARSETrrr UnicodeErrorrrr<rrrrr)r9r*r4rvrrrr`rr:rr> content_typeZ final_urlrencodingdecoderrrar,r,r-rsZ              &$ zSimpleScrapingLocator.get_pagez]*>([^<]+)r)rr,r-rZszDistPathLocator.__init__cCsP|jj|}|dkr iid}n,|j|d|jt|jgid|jtdgii}|S)N)rrrr)r>get_distributionrrr)r9rkrr`r,r,r-rls  zDistPathLocator._get_project)rBrCrDrErZrlrr,r,)rr-r;s r;csReZdZdZfddZfddZddZeej j eZ dd Z d d Z Z S) AggregatingLocatorzI This class allows you to chain and/or merge a list of locators. cs*|jdd|_||_tt|jf|dS)a Initialise an instance. :param locators: The list of locators to search. :param kwargs: Passed to the superclass constructor, except for: * merge - if False (the default), the first successful search from any of the locators is returned. If True, the results from all locators are merged (this can be slow). mergeFN)rrAlocatorsrr@rZ)r9rBr)rr,r-rZs zAggregatingLocator.__init__cs*tt|jx|jD] }|jqWdS)N)rr@rerB)r9r)rr,r-res zAggregatingLocator.clear_cachecCs ||_x|jD] }||_qWdS)N)rfrBr4)r9rhrr,r,r-ris zAggregatingLocator._set_schemec Csi}x|jD]}|j|}|r |jr|jdi}|jdi}|j||jd}|r|rx6|jD]*\}} ||kr||| O<qb| ||<qbW|jd} |r| r| j|q |jdkrd} n$d} x|D]}|jj|rd} PqW| r |}Pq W|S)NrrTF)rBrnrAr\updaterrWr) r9rkr`rrr3rZdfrrddfoundr,r,r-rls8           zAggregatingLocator._get_projectc Cs@t}x4|jD]*}y||jO}Wqtk r6YqXqW|S)zJ Return all the distribution names known to this locator. )rrBrmrj)r9r`rr,r,r-rms  z)AggregatingLocator.get_distribution_names)rBrCrDrErZrerirrIr4fgetrlrmrr,r,)rr-r@s  ,r@zhttps://pypi.org/simple/g@)r&legacy)r4z1(?P[\w-]+)\s*\(\s*(==\s*)?(?P[^)]+)\)$c@sLeZdZdZdddZddZddZd d Zd d Zd dZ dddZ dS)DependencyFinderz0 Locate dependencies for distributions. NcCs|pt|_t|jj|_dS)zf Initialise an instance, using the specified locator to locate distributions. N)default_locatorrr"r4)r9rr,r,r-rZ1s zDependencyFinder.__init__cCsvtjd||j}||j|<||j||jf<xD|jD]:}t|\}}tjd||||jj |t j ||fq4WdS)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) r{r|r@ dists_by_namedistsrprovidesrprovidedrrr)r9rrkprr,r,r-add_distribution9s    z!DependencyFinder.add_distributioncCs|tjd||j}|j|=|j||jf=xN|jD]D}t|\}}tjd||||j|}|j ||f|s0|j|=q0WdS)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) r{r|r@rJrKrrLrrMremove)r9rrkrNrsr,r,r-remove_distributionHs    z$DependencyFinder.remove_distributionc CsBy|jj|}Wn,tk r<|jd}|jj|}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)r4rWr#r)r9reqtrWrkr,r,r- get_matcherZs  zDependencyFinder.get_matcherc Csv|j|}|j}t}|j}||krrxL||D]@\}}y|j|}Wntk r\d}YnX|r.|j|Pq.W|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)rTr@rrMrr#r) r9rSrWrkr`rMrproviderrr,r,r-find_providersjs   zDependencyFinder.find_providersc Cs|j|}t}x,|D]$}|j|}|j|js|j|qW|r^|jd||t|fd}nD|j||j|=x"|D]}|jj|tj|qvW|j |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) reqtsrrTrrr frozensetrRrrO) r9rUotherproblemsZrlist unmatchedrQrWr`r,r,r-try_to_replaces"         zDependencyFinder.try_to_replaceFcCs i|_i|_i|_i|_t|p g}d|krH|jd|tdddgO}t|trh|}}tj d|n4|j j ||d}}|dkrt d|tj d |d |_ t}t|g}t|g}x|r|j}|j} | |jkr|j|n"|j| } | |kr |j|| ||j|jB} |j} t} |rh||krhx2dD]*}d|}||kr:| t|d|O} q:W| | B| B}x>|D]4}|j|}|sTtj d||j j ||d}|dkr| r|j j |d d}|dkrtj d||jd|fn^|j|j}}||f|jkr|j||j||| krT||krT|j|tj d|jxZ|D]R}|j} | |jkr|jj|tj|n"|j| } | |krZ|j|| |qZWq|WqWt|jj}x.|D]&}||k|_|jrtj d|jqWtj 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)rNzUnable to locate %rz located %sTtestbuilddevz:%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)r]r^r_)rMrKrJrWrrPr<rr{r|rrr requestedrr@rOr\Z run_requiresZ meta_requiresZbuild_requiresgetattrrVrrZname_and_versionrvaluesZbuild_time_dependency)r9rZ meta_extrasrrrrZtodoZ install_distsrkrYZireqtsZsreqtsZereqtsr@raZ all_reqtsrZ providersrUnrrNrKr,r,r-finds                               zDependencyFinder.find)N)NF) rBrCrDrErZrOrRrTrVr\rer,r,r,r-rH,s (rH)N)Oriorrloggingrrprr ImportErrordummy_threadingr(r2rcompatrrrrr r r r r rrr7rrrrZdatabaserrrrrrutilrrrrrrrr r!rr"r#rr$r% getLoggerrBr{rrrr"rr(r.r/objectrIrrrrr*r5r;r@rIrNAME_VERSION_RErHr,r,r,r-sZ   D,    G0E:zA&[