예제 #1
0
 def _find_candidates(self, requirement: Requirement) -> Iterable[Candidate]:
     sources = self.get_filtered_sources(requirement)
     with self.environment.get_finder(sources) as finder, allow_all_wheels():
         return [
             Candidate.from_installation_candidate(c, requirement, self.environment)
             for c in finder.find_all_candidates(requirement.project_name)
         ]
예제 #2
0
    def build(self,
              ireq: shims.InstallRequirement,
              hashes: Optional[Dict[str, str]] = None) -> str:
        """Build egg_info directory for editable candidates and a wheel for others.

        :param ireq: the InstallRequirment of the candidate.
        :param hashes: a dictionary of filename: hash_value to check against downloaded
        artifacts.
        :returns: The full path of the built artifact.
        """
        from pip._internal.utils.temp_dir import global_tempdir_manager
        from pdm.builders import EditableBuilder
        from pdm.builders import WheelBuilder

        kwargs = self._make_building_args(ireq)
        with self.get_finder() as finder:
            with allow_all_wheels():
                # temporarily allow all wheels to get a link.
                ireq.populate_link(finder, False, bool(hashes))
            if not ireq.editable and not ireq.req.name:
                ireq.source_dir = kwargs["build_dir"]
            else:
                ireq.ensure_has_source_dir(kwargs["build_dir"])

            download_dir = kwargs["download_dir"]
            only_download = False
            if ireq.link.is_wheel:
                download_dir = kwargs["wheel_download_dir"]
                only_download = True
            if hashes:
                ireq.options["hashes"] = convert_hashes(hashes)
            if not (ireq.editable and ireq.req.is_local_dir):
                with global_tempdir_manager():
                    downloaded = shims.shim_unpack(
                        link=ireq.link,
                        download_dir=download_dir,
                        location=ireq.source_dir,
                        hashes=ireq.hashes(False),
                        only_download=only_download,
                        session=finder.session,
                    )
                    # Preserve the downloaded file so that it won't be cleared.
                    if downloaded and only_download:
                        try:
                            shutil.copy(downloaded, download_dir)
                        except shutil.SameFileError:
                            pass
            # Now all source is prepared, build it.
            if ireq.link.is_wheel:
                return (context.cache("wheels") /
                        ireq.link.filename).as_posix()
            builder_class = EditableBuilder if ireq.editable else WheelBuilder
            kwargs["finder"] = finder
            with builder_class(ireq) as builder:
                return builder.build(**kwargs)
예제 #3
0
 def _find_candidates(self, requirement: Requirement) -> Iterable[Candidate]:
     sources = self.get_filtered_sources(requirement)
     with self.environment.get_finder(sources, True) as finder, allow_all_wheels():
         cans = [
             Candidate.from_installation_candidate(c, requirement)
             for c in finder.find_all_candidates(requirement.project_name)
         ]
     if not cans:
         raise CandidateNotFound(
             f"Unable to find candidates for {requirement.project_name}. There may "
             "exist some issues with the package name or network condition."
         )
     return cans
예제 #4
0
    def obtain(self, allow_all: bool = False) -> None:
        """Fetch the link of the candidate and unpack to local if necessary.

        :param allow_all: If true, don't validate the wheel tag nor hashes
        """
        ireq = self.ireq
        if self.wheel:
            if self._wheel_compatible(self.wheel, allow_all):
                return
        elif ireq.source_dir:
            return

        if not allow_all and self.candidate.hashes:
            ireq.hash_options = convert_hashes(self.candidate.hashes)
        with self.environment.get_finder(
                ignore_requires_python=True) as finder:
            if (not ireq.link or ireq.link.is_wheel and
                    not self._wheel_compatible(ireq.link.filename, allow_all)):
                ireq.link = self.wheel = None  # reset the incompatible wheel
                with allow_all_wheels(allow_all):
                    ireq.link = populate_link(finder, ireq, False)
                if not ireq.link:
                    raise CandidateNotFound("No candidate is found for %s",
                                            self)
                if not ireq.original_link:
                    ireq.original_link = ireq.link
            if allow_all and not self.req.editable:
                cached = self._get_cached_wheel()
                if cached:
                    self.wheel = cached.file_path
                    return
            downloader = pip_shims.Downloader(finder.session,
                                              "off")  # type: ignore
            self._populate_source_dir()
            if not ireq.link.is_existing_dir():
                assert ireq.source_dir
                downloaded = pip_shims.unpack_url(  # type: ignore
                    ireq.link,
                    ireq.source_dir,
                    downloader,
                    hashes=ireq.hashes(False),
                )
                if ireq.link.is_wheel:
                    assert downloaded
                    self.wheel = downloaded.path
                    return
예제 #5
0
 def get_hashes(self, candidate: Candidate) -> Optional[Dict[str, str]]:
     """Get hashes of all possible installable candidates of a given package version.
     """
     if (candidate.req.is_vcs or candidate.req.is_file_or_url
             and candidate.req.is_local_dir):
         return
     if candidate.hashes:
         return candidate.hashes
     req = candidate.req.copy()
     req.specifier = SpecifierSet(f"=={candidate.version}")
     with allow_all_wheels():
         matching_candidates = self.find_matches(req, allow_all=True)
     with self.environment.get_finder(self.sources) as finder:
         self._hash_cache.session = finder.session
         return {
             c.link.filename: self._hash_cache.get_hash(c.link)
             for c in matching_candidates
         }
예제 #6
0
    def find_candidates(
        self,
        requirement: Requirement,
        requires_python: PySpecSet = PySpecSet(),
        allow_prereleases: Optional[bool] = None,
        allow_all: bool = False,
    ) -> Iterable[Candidate]:
        sources = self.get_filtered_sources(requirement)
        # `allow_prereleases` is None means leave it to specifier to decide whether to
        # include prereleases
        if allow_prereleases is None:
            allow_prereleases = requirement.allow_prereleases

        with self.environment.get_finder(sources) as finder, allow_all_wheels():
            cans = [
                Candidate.from_installation_candidate(c, requirement, self.environment)
                for c in finder.find_all_candidates(requirement.project_name)
            ]
        sorted_cans = sorted(
            (
                c
                for c in cans
                if requirement.specifier.contains(c.version, allow_prereleases)
                and (allow_all or requires_python.is_subset(c.requires_python))
            ),
            key=lambda c: (c.version, c.link.is_wheel),
            reverse=True,
        )

        if not sorted_cans and allow_prereleases is None:
            # No non-pre-releases is found, force pre-releases now
            sorted_cans = sorted(
                (
                    c
                    for c in cans
                    if requirement.specifier.contains(c.version, True)
                    and (allow_all or requires_python.is_subset(c.requires_python))
                ),
                key=lambda c: c.version,
                reverse=True,
            )
        return sorted_cans
예제 #7
0
    def build(
        self,
        ireq: pip_shims.InstallRequirement,
        hashes: Optional[Dict[str, str]] = None,
        allow_all: bool = True,
    ) -> str:
        """Build egg_info directory for editable candidates and a wheel for others.

        :param ireq: the InstallRequirment of the candidate.
        :param hashes: a dictionary of filename: hash_value to check against downloaded
        artifacts.
        :param allow_all: Allow building incompatible wheels.
        :returns: The full path of the built artifact.
        """
        kwargs = self._make_building_args(ireq)
        wheel_cache = self.project.make_wheel_cache()
        with self.get_finder() as finder:
            with allow_all_wheels(allow_all):
                # temporarily allow all wheels to get a link.
                populate_link(finder, ireq, False)
                if hashes is None:
                    cache_entry = wheel_cache.get_cache_entry(
                        ireq.link,
                        ireq.req.project_name,
                        pip_shims.get_supported(
                            version="".join(
                                map(
                                    str,
                                    get_python_version(self.python_executable)[0][:2],
                                )
                            )
                        ),
                    )
                    if cache_entry is not None:
                        stream.logger.debug(
                            "Using cached wheel link: %s", cache_entry.link
                        )
                        ireq.link = cache_entry.link
            if not ireq.editable and not ireq.req.name:
                ireq.source_dir = kwargs["build_dir"]
            else:
                ireq.ensure_has_source_dir(kwargs["build_dir"])

            download_dir = kwargs["download_dir"]
            only_download = False
            if ireq.link.is_wheel:
                download_dir = kwargs["wheel_download_dir"]
                only_download = True
            if hashes:
                ireq.hash_options = convert_hashes(hashes)
            if not (ireq.editable and ireq.req.is_local_dir):
                downloader = pip_shims.Downloader(finder.session, "off")
                if ireq.link.is_vcs:
                    ireq.link = pip_shims.Link(expand_env_vars_in_auth(ireq.link.url))
                downloaded = pip_shims.unpack_url(
                    ireq.link,
                    ireq.source_dir,
                    downloader,
                    download_dir,
                    ireq.hashes(False),
                )
                # Preserve the downloaded file so that it won't be cleared.
                if downloaded and only_download:
                    try:
                        shutil.copy(downloaded.path, download_dir)
                    except shutil.SameFileError:
                        pass

            if ireq.link.is_wheel:
                # If the file is a wheel, should be already present under download dir.
                return (self.project.cache("wheels") / ireq.link.filename).as_posix()
            else:
                # Check the built wheel cache again after hashes are resolved.
                cache_entry = wheel_cache.get_cache_entry(
                    ireq.link,
                    ireq.req.project_name,
                    pip_shims.get_supported(
                        version="".join(
                            map(str, get_python_version(self.python_executable)[0][:2])
                        )
                    ),
                )
                if cache_entry is not None:
                    stream.logger.debug("Using cached wheel link: %s", cache_entry.link)
                    return cache_entry.link.file_path

            # Otherwise, now all source is prepared, build it.
            with EnvBuilder(ireq.unpacked_source_directory, self) as builder:
                if ireq.editable:
                    ret = builder.build_egg_info(kwargs["build_dir"])
                    ireq.metadata_directory = ret
                else:
                    should_cache = False
                    if ireq.link.is_vcs:
                        vcs = pip_shims.VcsSupport()
                        vcs_backend = vcs.get_backend_for_scheme(ireq.link.scheme)
                        if vcs_backend.is_immutable_rev_checkout(
                            ireq.link.url, ireq.source_dir
                        ):
                            should_cache = True
                    else:
                        base, _ = ireq.link.splitext()
                        if _egg_info_re.search(base) is not None:
                            # Determine whether the string looks like an egg_info.
                            should_cache = True
                    output_dir = (
                        wheel_cache.get_path_for_link(ireq.link)
                        if should_cache
                        else kwargs["build_dir"]
                    )
                    if not os.path.exists(output_dir):
                        os.makedirs(output_dir, exist_ok=True)
                    ret = builder.build_wheel(output_dir)
            return ret
예제 #8
0
    def build(
        self,
        ireq: pip_shims.InstallRequirement,
        hashes: Optional[Dict[str, str]] = None,
        allow_all: bool = True,
    ) -> str:
        """Build egg_info directory for editable candidates and a wheel for others.

        :param ireq: the InstallRequirment of the candidate.
        :param hashes: a dictionary of filename: hash_value to check against downloaded
        artifacts.
        :param allow_all: Allow building incompatible wheels.
        :returns: The full path of the built artifact.
        """
        build_dir = self._get_build_dir(ireq)
        wheel_cache = self.project.make_wheel_cache()
        supported_tags = pip_shims.get_supported("".join(
            map(str,
                get_python_version(self.python_executable, digits=2)[0])))
        with self.get_finder(ignore_requires_python=True) as finder:
            with allow_all_wheels(allow_all):
                # temporarily allow all wheels to get a link.
                populate_link(finder, ireq, False)
            ireq.link = pip_shims.Link(
                expand_env_vars_in_auth(
                    ireq.link.url.replace(
                        "${PROJECT_ROOT}",
                        self.project.root.as_posix().lstrip("/"))))
            if hashes is None and not ireq.editable:
                # If hashes are not given and cache is hit, replace the link with the
                # cached one. This can speed up by skipping the download and build.
                cache_entry = wheel_cache.get_cache_entry(
                    ireq.link,
                    ireq.req.project_name,
                    supported_tags,
                )
                if cache_entry is not None:
                    termui.logger.debug("Using cached wheel link: %s",
                                        cache_entry.link)
                    ireq.link = cache_entry.link
            if not ireq.editable and not ireq.req.name:
                ireq.source_dir = build_dir
            else:
                ireq.ensure_has_source_dir(build_dir)

            if hashes:
                ireq.hash_options = convert_hashes(hashes)
            if not (ireq.editable and ireq.req.is_local_dir):
                downloader = pip_shims.Downloader(finder.session, "off")
                downloaded = pip_shims.unpack_url(
                    ireq.link,
                    ireq.source_dir,
                    downloader,
                    hashes=ireq.hashes(False),
                )

                if ireq.link.is_wheel:
                    # If the file is a wheel, return the downloaded file directly.
                    return downloaded.path

        # Check the built wheel cache again after hashes are resolved.
        if not ireq.editable:
            cache_entry = wheel_cache.get_cache_entry(
                ireq.link,
                ireq.req.project_name,
                supported_tags,
            )
            if cache_entry is not None:
                termui.logger.debug("Using cached wheel link: %s",
                                    cache_entry.link)
                return cache_entry.link.file_path

        # Otherwise, as all source is already prepared, build it.
        with EnvBuilder(ireq.unpacked_source_directory, self) as builder:
            if ireq.editable:
                ret = builder.build_egg_info(build_dir)
                ireq.metadata_directory = ret
                return ret
            should_cache = False
            if ireq.link.is_vcs:
                vcs = pip_shims.VcsSupport()
                vcs_backend = vcs.get_backend_for_scheme(ireq.link.scheme)
                if vcs_backend.is_immutable_rev_checkout(
                        ireq.link.url, ireq.source_dir):
                    should_cache = True
            else:
                base, _ = ireq.link.splitext()
                if _egg_info_re.search(base) is not None:
                    # Determine whether the string looks like an egg_info.
                    should_cache = True
            output_dir = (wheel_cache.get_path_for_link(ireq.link)
                          if should_cache else build_dir)
            if not os.path.exists(output_dir):
                os.makedirs(output_dir, exist_ok=True)
            return builder.build_wheel(output_dir)
예제 #9
0
    def build(
        self,
        ireq: shims.InstallRequirement,
        hashes: Optional[Dict[str, str]] = None,
        allow_all: bool = True,
    ) -> str:
        """Build egg_info directory for editable candidates and a wheel for others.

        :param ireq: the InstallRequirment of the candidate.
        :param hashes: a dictionary of filename: hash_value to check against downloaded
        artifacts.
        :param allow_all: Allow building incompatible wheels.
        :returns: The full path of the built artifact.
        """
        from pdm.builders import EditableBuilder, WheelBuilder

        kwargs = self._make_building_args(ireq)
        with self.get_finder() as finder:
            if allow_all:
                with allow_all_wheels():
                    # temporarily allow all wheels to get a link.
                    populate_link(finder, ireq, False)
            else:
                populate_link(finder, ireq, False)
            if not ireq.editable and not ireq.req.name:
                ireq.source_dir = kwargs["build_dir"]
            else:
                ireq.ensure_has_source_dir(kwargs["build_dir"])

            download_dir = kwargs["download_dir"]
            only_download = False
            if ireq.link.is_wheel:
                download_dir = kwargs["wheel_download_dir"]
                only_download = True
            if hashes:
                ireq.hash_options = convert_hashes(hashes)
            if not (ireq.editable and ireq.req.is_local_dir):
                downloader = shims.Downloader(finder.session, "off")
                downloaded = shims.unpack_url(
                    ireq.link,
                    ireq.source_dir,
                    downloader,
                    download_dir,
                    ireq.hashes(False),
                )
                # Preserve the downloaded file so that it won't be cleared.
                if downloaded and only_download:
                    try:
                        shutil.copy(downloaded.path, download_dir)
                    except shutil.SameFileError:
                        pass
            # Now all source is prepared, build it.
            if ireq.link.is_wheel:
                return (self.project.cache("wheels") /
                        ireq.link.filename).as_posix()
            self.ensure_essential_packages()
            if ireq.editable:
                builder_class = EditableBuilder
            else:
                builder_class = WheelBuilder
            kwargs["finder"] = finder
            with builder_class(ireq) as builder, self.activate(True):
                return builder.build(**kwargs)
예제 #10
0
    def build(
        self,
        ireq: pip_shims.InstallRequirement,
        hashes: dict[str, str] | None = None,
        allow_all: bool = True,
    ) -> str:
        """Build egg_info directory for editable candidates and a wheel for others.

        :param ireq: the InstallRequirment of the candidate.
        :param hashes: a dictionary of filename: hash_value to check against downloaded
        artifacts.
        :param allow_all: Allow building incompatible wheels.
        :returns: The full path of the built artifact.
        """
        from pdm.builders import EnvEggInfoBuilder, EnvWheelBuilder

        wheel_cache = self.project.make_wheel_cache()
        supported_tags = pip_shims.get_supported(self.interpreter.for_tag())
        if hashes:
            ireq.hash_options = convert_hashes(hashes)
        with self.get_finder(ignore_requires_python=True) as finder:
            with allow_all_wheels(allow_all):
                # temporarily allow all wheels to get a link.
                populate_link(finder, ireq, False)
            assert ireq.link
            if hashes is None and not ireq.editable:
                # If hashes are not given and cache is hit, replace the link with the
                # cached one. This can speed up by skipping the download and build.
                cache_entry = wheel_cache.get_cache_entry(
                    ireq.link,
                    ireq.req.project_name,  # type: ignore
                    supported_tags,
                )
                if cache_entry is not None:
                    termui.logger.debug("Using cached wheel link: %s",
                                        cache_entry.link)
                    ireq.link = cache_entry.link
            build_dir = self._get_build_dir(ireq)
            if not ireq.source_dir:
                ireq.source_dir = build_dir

            if not ireq.link.is_existing_dir():
                downloader = pip_shims.Downloader(finder.session,
                                                  "off")  # type: ignore
                downloaded = pip_shims.unpack_url(
                    ireq.link,
                    ireq.source_dir,
                    downloader,
                    hashes=ireq.hashes(False),
                )

                if ireq.link.is_wheel:
                    # If the file is a wheel, return the downloaded file directly.
                    assert downloaded
                    return downloaded.path

        # Check the built wheel cache again after hashes are resolved.
        if not ireq.editable:
            cache_entry = wheel_cache.get_cache_entry(
                ireq.link,
                ireq.req.project_name,  # type: ignore
                supported_tags,
            )
            if cache_entry is not None:
                termui.logger.debug("Using cached wheel link: %s",
                                    cache_entry.link)
                return cache_entry.link.file_path

        # Otherwise, as all source is already prepared, build it.
        output_dir = os.path.join(build_dir, "dist")
        if not os.path.exists(output_dir):
            os.makedirs(output_dir, exist_ok=True)
        if ireq.editable:
            builder = EnvEggInfoBuilder(ireq.unpacked_source_directory, self)
            ret = ireq.metadata_directory = builder.build(output_dir)
            return ret
        should_cache = False
        if ireq.link.is_vcs:
            vcs = pip_shims.VcsSupport()
            vcs_backend = vcs.get_backend_for_scheme(ireq.link.scheme)
            if vcs_backend and vcs_backend.is_immutable_rev_checkout(
                    ireq.link.url, ireq.source_dir):
                should_cache = True
        elif not ireq.link.is_existing_dir:
            base, _ = ireq.link.splitext()
            if _egg_info_re.search(base) is not None:
                # Determine whether the string looks like an egg_info.
                should_cache = True
        if should_cache:
            output_dir = wheel_cache.get_path_for_link(ireq.link)
        if not os.path.exists(output_dir):
            os.makedirs(output_dir, exist_ok=True)
        return EnvWheelBuilder(ireq.unpacked_source_directory,
                               self).build(output_dir)