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<qqn|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 |ƒdkr2g}|j } |D]z} | dkr†qxzH| | ¡s¢t d|| ¡n*|s°| | ƒjs¼| | ¡nt d| |j ¡Wqxtk rðt d|| ¡YqxXqxt |ƒd krt||jd }|r2t d |¡|d } || }|rš|jrH|j|_| d i¡ | tƒ¡|_i} | di¡} |jD]}|| krv| || |<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®rrœÚ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 kr4qt |j d} |j | _ | | _ t| ƒ}||_||| <| D]T} | d }|j |¡| | ¡|j|<|d  | tƒ¡ |¡| | ¡|d |<qhqWn@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,r5rmrirrr‘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œ}|jx||_||_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 krt  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|jdkr´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}}|dkrt d|ƒ‚t  d |¡d |_ tƒ}t|gƒ}t|gƒ}|r¤| ¡}|j} | |jkræ| |¡n"|j| } | |kr| || |¡|j|jB} |j} tƒ} |r`||kr`d D]*}d |}||kr4| t|d |ƒO} q4| | B| B}|D].}| |¡}|sFt  d|¡|j j ||d}|dkrÂ|sÂ|j j |d d}|dkrèt  d|¡| d|f¡n^|j|j}}||f|jkr| |¡| |¡|| krF||krF| |¡t  d|j¡|D]R}|j} | |jkrx|j |tƒ¡ |¡n"|j| } | |krJ| || |¡qJqpq¼t|j ¡ƒ}|D]&}||k|_|jr¶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 requestedrr?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Únrr2r/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&[ÿü