Path: blob/main/test/lib/python3.9/site-packages/pip/_internal/index/sources.py
4804 views
import logging1import mimetypes2import os3import pathlib4from typing import Callable, Iterable, Optional, Tuple56from pip._internal.models.candidate import InstallationCandidate7from pip._internal.models.link import Link8from pip._internal.utils.urls import path_to_url, url_to_path9from pip._internal.vcs import is_url1011logger = logging.getLogger(__name__)1213FoundCandidates = Iterable[InstallationCandidate]14FoundLinks = Iterable[Link]15CandidatesFromPage = Callable[[Link], Iterable[InstallationCandidate]]16PageValidator = Callable[[Link], bool]171819class LinkSource:20@property21def link(self) -> Optional[Link]:22"""Returns the underlying link, if there's one."""23raise NotImplementedError()2425def page_candidates(self) -> FoundCandidates:26"""Candidates found by parsing an archive listing HTML file."""27raise NotImplementedError()2829def file_links(self) -> FoundLinks:30"""Links found by specifying archives directly."""31raise NotImplementedError()323334def _is_html_file(file_url: str) -> bool:35return mimetypes.guess_type(file_url, strict=False)[0] == "text/html"363738class _FlatDirectorySource(LinkSource):39"""Link source specified by ``--find-links=<path-to-dir>``.4041This looks the content of the directory, and returns:4243* ``page_candidates``: Links listed on each HTML file in the directory.44* ``file_candidates``: Archives in the directory.45"""4647def __init__(48self,49candidates_from_page: CandidatesFromPage,50path: str,51) -> None:52self._candidates_from_page = candidates_from_page53self._path = pathlib.Path(os.path.realpath(path))5455@property56def link(self) -> Optional[Link]:57return None5859def page_candidates(self) -> FoundCandidates:60for path in self._path.iterdir():61url = path_to_url(str(path))62if not _is_html_file(url):63continue64yield from self._candidates_from_page(Link(url))6566def file_links(self) -> FoundLinks:67for path in self._path.iterdir():68url = path_to_url(str(path))69if _is_html_file(url):70continue71yield Link(url)727374class _LocalFileSource(LinkSource):75"""``--find-links=<path-or-url>`` or ``--[extra-]index-url=<path-or-url>``.7677If a URL is supplied, it must be a ``file:`` URL. If a path is supplied to78the option, it is converted to a URL first. This returns:7980* ``page_candidates``: Links listed on an HTML file.81* ``file_candidates``: The non-HTML file.82"""8384def __init__(85self,86candidates_from_page: CandidatesFromPage,87link: Link,88) -> None:89self._candidates_from_page = candidates_from_page90self._link = link9192@property93def link(self) -> Optional[Link]:94return self._link9596def page_candidates(self) -> FoundCandidates:97if not _is_html_file(self._link.url):98return99yield from self._candidates_from_page(self._link)100101def file_links(self) -> FoundLinks:102if _is_html_file(self._link.url):103return104yield self._link105106107class _RemoteFileSource(LinkSource):108"""``--find-links=<url>`` or ``--[extra-]index-url=<url>``.109110This returns:111112* ``page_candidates``: Links listed on an HTML file.113* ``file_candidates``: The non-HTML file.114"""115116def __init__(117self,118candidates_from_page: CandidatesFromPage,119page_validator: PageValidator,120link: Link,121) -> None:122self._candidates_from_page = candidates_from_page123self._page_validator = page_validator124self._link = link125126@property127def link(self) -> Optional[Link]:128return self._link129130def page_candidates(self) -> FoundCandidates:131if not self._page_validator(self._link):132return133yield from self._candidates_from_page(self._link)134135def file_links(self) -> FoundLinks:136yield self._link137138139class _IndexDirectorySource(LinkSource):140"""``--[extra-]index-url=<path-to-directory>``.141142This is treated like a remote URL; ``candidates_from_page`` contains logic143for this by appending ``index.html`` to the link.144"""145146def __init__(147self,148candidates_from_page: CandidatesFromPage,149link: Link,150) -> None:151self._candidates_from_page = candidates_from_page152self._link = link153154@property155def link(self) -> Optional[Link]:156return self._link157158def page_candidates(self) -> FoundCandidates:159yield from self._candidates_from_page(self._link)160161def file_links(self) -> FoundLinks:162return ()163164165def build_source(166location: str,167*,168candidates_from_page: CandidatesFromPage,169page_validator: PageValidator,170expand_dir: bool,171cache_link_parsing: bool,172) -> Tuple[Optional[str], Optional[LinkSource]]:173174path: Optional[str] = None175url: Optional[str] = None176if os.path.exists(location): # Is a local path.177url = path_to_url(location)178path = location179elif location.startswith("file:"): # A file: URL.180url = location181path = url_to_path(location)182elif is_url(location):183url = location184185if url is None:186msg = (187"Location '%s' is ignored: "188"it is either a non-existing path or lacks a specific scheme."189)190logger.warning(msg, location)191return (None, None)192193if path is None:194source: LinkSource = _RemoteFileSource(195candidates_from_page=candidates_from_page,196page_validator=page_validator,197link=Link(url, cache_link_parsing=cache_link_parsing),198)199return (url, source)200201if os.path.isdir(path):202if expand_dir:203source = _FlatDirectorySource(204candidates_from_page=candidates_from_page,205path=path,206)207else:208source = _IndexDirectorySource(209candidates_from_page=candidates_from_page,210link=Link(url, cache_link_parsing=cache_link_parsing),211)212return (url, source)213elif os.path.isfile(path):214source = _LocalFileSource(215candidates_from_page=candidates_from_page,216link=Link(url, cache_link_parsing=cache_link_parsing),217)218return (url, source)219logger.warning(220"Location '%s' is ignored: it is neither a file nor a directory.",221location,222)223return (url, None)224225226