3 ig@s2ddlZddlmZddlZddlZddlZddlZddlZy ddlZWne k rdddl ZYnXddl Z ddl m Z ddlmZmZmZmZmZmZmZmZmZmZmZmZmZmZddlmZm 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-dd l.m/Z/m0Z0dd l1m2Z2m3Z3ej4e5Z6ej7d Z8ej7d ej9Z:ej7d Z;dZGddde?Z@Gddde@ZAGddde@ZBGddde?ZCGddde@ZDGddde@ZEGdd d e@ZFGd!d"d"e@ZGGd#d$d$e@ZHeHeFeDd%d&d'd(d)ZIeIjJZJGd*d+d+e?ZKdS)-N)BytesIO)DistlibException)urljoinurlparse urlunparse url2pathname pathname2urlqueuequoteunescape build_openerHTTPRedirectHandler text_typeRequest HTTPErrorURLError) DistributionDistributionPath make_dist)MetadataMetadataInvalidError)cached_property 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*;/tmp/pip-build-88gy_88q/pip/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)r.r/)rschemer get_full_urlhasattrr1BaseRedirectHandlerhttp_error_302) selfreqfpcodemsgheadersnewurlkeyurlpartsr*r*r+r6@s   zRedirectHandler.http_error_302N)__name__ __module__ __qualname____doc__r6http_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) _cacher2r r-openermatcherr Queueerrors)r7r2r*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)rWemptygetappendEmpty task_done)r7resulter*r*r+ get_errorsys  zLocator.get_errorscCs |jdS)z> Clear any errors which may have been logged. N)r`)r7r*r*r+ clear_errorsszLocator.clear_errorscCs|jjdS)N)rSclear)r7r*r*r+ clear_cacheszLocator.clear_cachecCs|jS)N)_scheme)r7r*r*r+ _get_schemeszLocator._get_schemecCs ||_dS)N)rd)r7valuer*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)r7namer*r*r+ _get_projects zLocator._get_projectcCs tddS)zJ Return all the distribution names known to this locator. z Please implement in the subclassN)rh)r7r*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)rSrjra)r7rir^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_tagsr2netloc)r7r(tro 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)rxloggerdebug)r7url1url2r^s1s2r*r*r+ prefer_urls   zLocator.prefer_urlcCs t||S)zZ Attempt to split a filename in project name, version and Python version. )r)r7filename 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: %sTr0z, cSs"g|]}djt|ddqS).N)joinlist).0vr*r*r+ sz8Locator.convert_url_to_download_info..)riversionrr(zpython-versionzinvalid path for wheel: %szNot downloadable: %sz No match for project/version: %s)rirrr(zpython-versionz %s_digest)NNr)rlower startswithryrz HASHER_HASHmatchgroupsrqr"r#rsrirrrrpyver Exceptionwarningrrrnrolenr)r7r(rrr^r2rtrpparamsqueryfragmalgodigestZorigpathwheelincluder_rextrurirrr*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*)r7infor^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. rir)r2r(rurlsN) popmetadatarr2rr source_urlr setdefaultsetaddlocator) r7r^rrirdistmdrr(r*r*r+_update_version_dataHs   zLocator._update_version_dataFc Csd}t|}|dkr td|t|j}|j|j|_}tjd|t|j |j |j }t |dkrg}|j } xd|D]\} | d krqzy*|j| sn|s| | j r|j| Wqztk rtjd|| YqzXqzWt |dkrt||jd }|rtjd ||d } || }|r|jr,|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)rrrzerror matching %s with %rr)r>zsorted list: %s)rrr)rrr r2rU requirementryrztyper@rlrirZ version_classr is_prereleaser[rrsortedr>extrasrZr download_urlsr)r7r prereleasesr^rr2rUversionsslistZvclskrdsdr(r*r*r+locate_sP            zLocator.locate)rHrIrJrKrLrM)rNrOrP)rQ)rP)rR)F)r@rArBrCsource_extensionsbinary_extensionsexcluded_extensionsrsrrrXr`rarcrergpropertyr2rjrkrlrxrrrrrrr*r*r*r+rGVs.   JrGcs0eZdZdZfddZddZddZZS)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)superrrXbase_urlrr))r7r(kwargs) __class__r*r+rXszPyPIRPCLocator.__init__cCst|jjS)zJ Return all the distribution names known to this locator. )rr)r')r7r*r*r+rksz%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)r2rirlicensekeywordssummaryrr(rr)r)Zpackage_releasesZ release_urlsZ release_datarr2rirrZrrrrrrrrrrr) r7rir^rrrdatarrrr(rr*r*r+rjs0           zPyPIRPCLocator._get_project)r@rArBrCrXrkrj __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)rrrXrr)r7r(r)rr*r+rXszPyPIJSONLocator.__init__cCs tddS)zJ Return all the distribution names known to this locator. zNot available from this locatorN)rh)r7r*r*r+rksz&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)r2rrirrrrrr(rreleaseszJSON fetch failed: %s) rrr rTopenreaddecodejsonloadsrr2rirrZrrrrrrrrrrritemsrrWputrry exception)r7rir^r(resprrrrrrrZinfosZomdodistr_r*r*r+rjsT               " zPyPIJSONLocator._get_project)r@rArBrCrXrkrjrr*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)r7rr(rr*r*r+rX 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(r2rtrprrrr*r*r+clean4s zPage.links..cleanr0Zrel1Zrel2Zrel3Zrel4Zrel5Zrel6r{r|Zurl3cSsdt|jdS)Nz%%%2xr)ordr)rr*r*r+BszPage.links..cSs|dS)Nrr*)rur*r*r+rFsT)r>reverse) r_hreffinditerr groupdictrrr _clean_resubrr)r7rr^rrrelr(r*r*r+links-s  z Page.linksN)r@rArBrCrecompileISXrrrXrrrr*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. cCstjt|djS)N)fileobj)gzipGzipFilerr)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)rrrXrrr$ _page_cacher_seenr rV _to_fetch _bad_hostsskip_externals num_workers threadingRLock_lock_gplockplatform_check)r7r(r$rr)rr*r+rXXs     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[)r7irur*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)r7rur*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 rrbrrryrzrrrr)r7rir^r(r*r*r+rjs      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)r7r(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 rrryrzrrr^)r7r(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. Fhomepagedownloadhttprmftp:rr localhostTz#should_queue: %s (%s) from %s -> %s)r r )rrmr) rrqrrrrrrr splitrryrz) r7linkZreferrerrr2rtrp_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)rrZget_pagerrrr rryrzrrrrWrr])r7r(pagerrr_r*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-Typer0zContent-Encodingzutf-8zlatin-1izFetch failed: %s: %s)$rosrpisdirrrrrryrzrrrrTrr$rrZHTML_CONTENT_TYPErgeturlrdecodersCHARSETrrr UnicodeErrorrrr:rrrrr)r7r(r2rtrprr^rr8rr< content_typeZ final_urlrencodingdecoderrr_r*r*r+rsZ              &$ zSimpleScrapingLocator.get_pagez]*>([^<]+)rX)r7r@r)rr*r+rXs zAggregatingLocator.__init__cs*tt|jx|jD] }|jqWdS)N)rr>rcr@)r7r)rr*r+rcs zAggregatingLocator.clear_cachecCs ||_x|jD] }||_qWdS)N)rdr@r2)r7rfrr*r*r+rgs 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)r@rlr?rZupdaterrUr) r7rir^rrr1rZdfrrddfoundr*r*r+rjs8           zAggregatingLocator._get_projectc Cs@t}x4|jD]*}y||jO}Wqtk r6YqXqW|S)zJ Return all the distribution names known to this locator. )rr@rkrh)r7r^rr*r*r+rks  z)AggregatingLocator.get_distribution_names)r@rArBrCrXrcrgrrGr2fgetrjrkrr*r*)rr+r>s  ,r>zhttps://pypi.org/simple/g@)r$legacy)r2c@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 r2)r7rr*r*r+rX/s 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) ryrzr> dists_by_namedistsrprovidesrprovidedrrr)r7rriprr*r*r+add_distribution7s    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) ryrzr>rHrIrrJrrKremove)r7rrirLrsr*r*r+remove_distributionFs    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)r2rUr!r)r7reqtrUrir*r*r+ get_matcherXs  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)rRr>rrKrr!r) r7rQrUrir^rKrproviderrr*r*r+find_providershs   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) reqtsrrRrrr frozensetrPrrM) r7rSotherproblemsZrlist unmatchedrOrUr^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])rKrIrHrUrrNr:rryrzrrr requestedrr>rMrZZ run_requiresZ meta_requiresZbuild_requiresgetattrrTrrZname_and_versionrvaluesZbuild_time_dependency)r7rZ meta_extrasrrrrXtodoZ install_distsrirWZireqtsZsreqtsZereqtsr>r_Z all_reqtsrZ providersrSnrrLrIr*r*r+finds                               zDependencyFinder.find)N)NF) r@rArBrCrXrMrPrRrTrZrcr*r*r*r+rF*s (rF)N)Lriorrloggingrrnrr ImportErrordummy_threadingr&r0rcompatrrrrr r r r r rr5rrrrZdatabaserrrrrrutilrrrrrrrrrr r!rr"r# getLoggerr@ryrrrr rr&r,r-objectrGrrrrr(r3r9r>rGrrFr*r*r*r+sX   @(    G0E:zA&[