Re@s0ddlZddlmZddlZddlZddlZddlZddlZyddlZWne k rddl 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;Z<ej9d Z=dZ>dddZ?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 CsG|dkrt}t|dd}z|jSWd|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. Ntimeoutg@close) DEFAULT_INDEXr list_packages)urlclientr,/builddir/build/BUILDROOT/alt-python35-pip-20.2.4-5.el8.x86_64/opt/alt/python35/lib/python3.5/site-packages/pip/_vendor/distlib/locators.pyget_all_distribution_names)s  r.c@s0eZdZdZddZeZZZdS)RedirectHandlerzE A class to work around a bug in some Python 3.2.x releases. c Csd}x%dD]}||kr ||}Pq W|dkr>dSt|}|jdkrt|j|}t|dr|j||n ||| 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_schemecCstddS)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_projectcCstddS)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_namescCsj|jdkr!|j|}nE||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_projectcCst|}tj|j}d}|jd}|j|j}|rctt||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_urlcCsu|}|rq|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_filenamecCsdd}d}t|\}}}}} } | jjdrXtjd|| tj| } | r| j\} } n d\} } |}|r|ddkr|dd}|jdryt |}t ||j stjd |n|dkrd }n||j |}|rd |j d |j d |jdt||||| dfddjdd|jDi}Wqtk r}ztjd|WYdd}~XqXn |j|jstjd|ntj|}}x|jD]}|j|r|dt| }|j||}|s]tjd|nk|\}}}| s|||rd |d |d |dt||||| dfi}|r||d.same_projectNzegg=z %s: version hint in fragment: %rr/z.whlzWheel not compatible: %sTrkversionrr*r2zpython-versionz, cSs/g|]%}djt|ddqS).N)joinlist).0vr,r,r- s z8Locator.convert_url_to_download_info..zinvalid path for wheel: %szNot downloadable: %sz No match for project/version: %sz %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_infocCsd}d|krJ|d}x+dD]#}||kr#|||f}Pq#W|sx5dD]-}d|}||krW|||f}PqWW|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}n!t||d|j}|j}|j||_}|d}||d|<|j|dkr|j|j||_|dj|t j |||_ |||d}t|}|dkr.td|t|j}|j|j|_}tjd|t|j |j |j }t |dkrg}|j } x|D]} | d krqye|j| stjd|| n<|s| | j r|j| ntjd| |j Wqtk rMtjd || YqXqWt |d kryt|d |j}|rtjd ||d} || }|r1|jr|j|_|jdij| t|_i} |jdi} x+|jD] }|| kr| || |= 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 %rrr@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)rBrCrDrEsource_extensionsbinary_extensionsexcluded_extensionsrurtrZrbrcrergripropertyr4rlrmrnrzrrrrrrr,r,r,r-rIVs.             J  rIcs@eZdZdZfddZddZddZS)PyPIRPCLocatorz This locator uses XML-RPC to locate distributions. It therefore cannot be used with simple mirrors (that only mirror file content). c s8tt|j|||_t|dd|_dS)z Initialise an instance. :param url: The URL to use for XML-RPC. :param kwargs: Passed to the superclass constructor. r&g@N)superrrZbase_urlr r+)r9r*kwargs) __class__r,r-rZs zPyPIRPCLocator.__init__cCst|jjS)zJ Return all the distribution names known to this locator. )rr+r))r9r,r,r-rmsz%PyPIRPCLocator.get_distribution_namesc Csqdidii}|jj|d}xC|D];}|jj||}|jj||}td|j}|d|_|d|_|jd|_ |jdg|_ |jd |_ t |}|r.|d } | d |_ |j| |_||_|||[^"]*)"|'(?P[^']*)'|(?P[^>\s ]*))\s+)? href\s*=\s*(?:"(?P[^"]*)"|'(?P[^']*)'|(?P[^>\s ]*)) (\s+rel\s*=\s*(?:"(?P[^"]*)"|'(?P[^']*)'|(?P[^>\s ]*)))? z!]+)cCsJ||_||_|_|jj|j}|rF|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$&+,/:;=?@.#%_\\|-]cCs dd}t}x|jj|jD]}|jd}|dpy|dpy|dpy|dpy|dpy|d }|d p|d p|d }t|j|}t|}|jj d d|}|j ||fq+Wt |ddddd}|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-clean4szPage.links..cleanr2Zrel1Zrel2Zrel3Zrel4Zrel5Zrel6r}r~Zurl3cSsdt|jdS)Nz%%%2xr)ordr)rr,r,r-BszPage.links..r@cSs|dS)Nrr,)rwr,r,r-rFsreverseT) r_hreffinditerr groupdictrrr _clean_resubrr)r9rr`rrrelr*r,r,r-links-s   z Page.linksN)rBrCrDrErecompileISXrrrZrrrr,r,r,r-rs  rcseZdZdZdejddddddiZdd fd d Zd d ZddZ ddZ e j de j ZddZddZddZddZddZe j dZddZS) 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. deflategzipcCstjdttjS)Nfileobj)rGzipFilerrr)br,r,r-rTszSimpleScrapingLocator.nonecCs|S)Nr,)rr,r,r-rUsN c stt|j|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__cCscg|_xSt|jD]B}tjd|j}|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_threadscCsOx!|jD]}|jjdq Wx|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_threadscCsdidii}|j||_||_t|jdt|}|jj|jj|j z1t j d||j j ||j jWd|jX|`WdQRX|S)Nrrz%s/z Queueing %s)rr`rrrr rrdrrr{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 Csw|jr!|j|r!d}n|j||j}tjd|||rs|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|jrGd}n|jrl|j|j rld}n|j|jsd}ny|d krd}nd|dkrd}nO|j|rd}n7|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)r r)rror) rrsrrrrrrr splitrr{r|) r9linkZreferrerrr4rvrr_r`hostr,r,r- _should_queues*           z#SimpleScrapingLocator._should_queuecCs6x/|jj}zy|r|j|}|dkr<wx|jD]\}}||jkrFy\|jj||j| r|j|||rtj d|||jj |WqFt k rYqFXqFWWn;t 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._fetchcCst|\}}}}}}|dkrWtjjt|rWtt|d}||jkr|j|}tj d||nv|j ddd}d}||j krtj d||n5t |d d d i}z y/tj d ||j j|d |j} tj d|| j} | jdd} tj| r| j} | j} | jd}|r|j|}|| } d}tj| }|r|jd}y| j|} Wn!tk r| jd} YnXt| | }||j| zAccept-encodingidentityz Fetching %sr&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]*>([^<]+)[\w-]+)\s*\(\s*(==\s*)?(?P[^)]+)\)$c@sseZdZdZdddZddZddZd d Zd d Zd dZ ddddZ dS)DependencyFinderz0 Locate dependencies for distributions. NcCs(|p t|_t|jj|_dS)zf Initialise an instance, using the specified locator to locate distributions. N)default_locatorrr"r4)r9rr,r,r-rZ1szDependencyFinder.__init__cCstjd||j}||j|<||j||jf= 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_replaceFcCsi|_i|_i|_i|_t|p0g}d|krh|jd|tdddgO}t|tr|}}tj d|nH|j j |d|}}|dkrt d|tj d |d |_ t}t|g}t|g}x|r|j}|j} | |jkrI|j|n,|j| } | |kru|j|| ||j|jB} |j} t} |r||krx;dD]3}d|}||kr| t|d|O} qW| | B| B}x|D]}|j|}|stj d||j j |d|}|dkrj| rj|j j |dd }|dkrtj d||jd|fnz|j|j}}||f|jkr|j||j||| kr||kr|j|tj d|jxt|D]l}|j} | |jkr]|jj|tj|q|j| } | |kr|j|| |qWqWqWt|jj}x9|D]1}||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 requirementrNzUnable 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) rBrCrDrErZrOrRrTrVr\rer,r,r,r-rH,s      (rH)Oriorrloggingrrprr ImportErrordummy_threadingr'r2rcompatrrrrr r r r r rrr7rrrrZdatabaserrrrrrutilrrrrrrrr r!rr"r#rr$r% getLoggerrBr{rrrr!rr(r.r/objectrIrrrrr)r4r:r?rIrNAME_VERSION_RErHr,r,r,r-sV        d@F0E:A&[