Beispiel #1
0
 def test_supported_osx_version(self) -> None:
     """
     Wheels built for macOS 10.6 are supported on 10.9
     """
     tags = compatibility_tags.get_supported(
         "27", platforms=["macosx_10_9_intel"], impl="cp")
     w = Wheel("simple-0.1-cp27-none-macosx_10_6_intel.whl")
     assert w.supported(tags=tags)
     w = Wheel("simple-0.1-cp27-none-macosx_10_9_intel.whl")
     assert w.supported(tags=tags)
Beispiel #2
0
 def test_supported_osx_version(self):
     """
     Wheels built for macOS 10.6 are supported on 10.9
     """
     tags = compatibility_tags.get_supported(
         '27', platforms=['macosx_10_9_intel'], impl='cp')
     w = Wheel('simple-0.1-cp27-none-macosx_10_6_intel.whl')
     assert w.supported(tags=tags)
     w = Wheel('simple-0.1-cp27-none-macosx_10_9_intel.whl')
     assert w.supported(tags=tags)
Beispiel #3
0
    def test_not_supported_multiarch_darwin(self):
        """
        Single-arch wheels (x86_64) are not supported on multi-arch (intel)
        """
        universal = compatibility_tags.get_supported(
            '27', platforms=['macosx_10_5_universal'], impl='cp')
        intel = compatibility_tags.get_supported(
            '27', platforms=['macosx_10_5_intel'], impl='cp')

        w = Wheel('simple-0.1-cp27-none-macosx_10_5_i386.whl')
        assert not w.supported(tags=intel)
        assert not w.supported(tags=universal)
        w = Wheel('simple-0.1-cp27-none-macosx_10_5_x86_64.whl')
        assert not w.supported(tags=intel)
        assert not w.supported(tags=universal)
Beispiel #4
0
    def test_not_supported_multiarch_darwin(self) -> None:
        """
        Single-arch wheels (x86_64) are not supported on multi-arch (intel)
        """
        universal = compatibility_tags.get_supported(
            "27", platforms=["macosx_10_5_universal"], impl="cp")
        intel = compatibility_tags.get_supported(
            "27", platforms=["macosx_10_5_intel"], impl="cp")

        w = Wheel("simple-0.1-cp27-none-macosx_10_5_i386.whl")
        assert not w.supported(tags=intel)
        assert not w.supported(tags=universal)
        w = Wheel("simple-0.1-cp27-none-macosx_10_5_x86_64.whl")
        assert not w.supported(tags=intel)
        assert not w.supported(tags=universal)
Beispiel #5
0
    def get(
            self,
            link,  # type: Link
            package_name,  # type: Optional[str]
            supported_tags,  # type: List[Pep425Tag]
    ):
        # type: (...) -> Link
        candidates = []

        if not package_name:
            return link

        canonical_package_name = canonicalize_name(package_name)
        for wheel_name in self._get_candidates(link, canonical_package_name):
            try:
                wheel = Wheel(wheel_name)
            except InvalidWheelFilename:
                continue
            if canonicalize_name(wheel.name) != canonical_package_name:
                logger.debug(
                    "Ignoring cached wheel {} for {} as it "
                    "does not match the expected distribution name {}.".format(
                        wheel_name, link, package_name))
                continue
            if not wheel.supported(supported_tags):
                # Built for a different python/arch/etc
                continue
            candidates.append(
                (wheel.support_index_min(supported_tags), wheel_name))

        if not candidates:
            return link

        return self._link_for_candidate(link, min(candidates)[1])
Beispiel #6
0
 def make_requirement_from_install_req(self, ireq, requested_extras):
     # type: (InstallRequirement, Iterable[str]) -> Optional[Requirement]
     if not ireq.match_markers(requested_extras):
         logger.info(
             "Ignoring %s: markers '%s' don't match your environment",
             ireq.name,
             ireq.markers,
         )
         return None
     if not ireq.link:
         return SpecifierRequirement(ireq)
     if ireq.link.is_wheel:
         wheel = Wheel(ireq.link.filename)
         if not wheel.supported(self._finder.target_python.get_tags()):
             msg = "{} is not a supported wheel on this platform.".format(
                 wheel.filename, )
             raise UnsupportedWheel(msg)
     cand = self._make_candidate_from_link(
         ireq.link,
         extras=frozenset(ireq.extras),
         template=ireq,
         name=canonicalize_name(ireq.name) if ireq.name else None,
         version=None,
     )
     if cand is None:
         # There's no way we can satisfy a URL requirement if the underlying
         # candidate fails to build. An unnamed URL must be user-supplied, so
         # we fail eagerly. If the URL is named, an unsatisfiable requirement
         # can make the resolver do the right thing, either backtrack (and
         # maybe find some other requirement that's buildable) or raise a
         # ResolutionImpossible eventually.
         if not ireq.name:
             raise self._build_failures[ireq.link]
         return UnsatisfiableRequirement(canonicalize_name(ireq.name))
     return self.make_requirement_from_candidate(cand)
Beispiel #7
0
 def make_requirement_from_install_req(self, ireq, requested_extras):
     # type: (InstallRequirement, Iterable[str]) -> Optional[Requirement]
     if not ireq.match_markers(requested_extras):
         logger.info(
             "Ignoring %s: markers '%s' don't match your environment",
             ireq.name, ireq.markers,
         )
         return None
     if not ireq.link:
         return SpecifierRequirement(ireq)
     if ireq.link.is_wheel:
         wheel = Wheel(ireq.link.filename)
         if not wheel.supported(self._finder.target_python.get_tags()):
             msg = "{} is not a supported wheel on this platform.".format(
                 wheel.filename,
             )
             raise UnsupportedWheel(msg)
     cand = self._make_candidate_from_link(
         ireq.link,
         extras=frozenset(ireq.extras),
         template=ireq,
         name=canonicalize_name(ireq.name) if ireq.name else None,
         version=None,
     )
     return self.make_requirement_from_candidate(cand)
Beispiel #8
0
    def _sort_key(self, candidate):
        # type: (InstallationCandidate) -> CandidateSortingKey
        """
        Function to pass as the `key` argument to a call to sorted() to sort
        InstallationCandidates by preference.

        Returns a tuple such that tuples sorting as greater using Python's
        default comparison operator are more preferred.

        The preference is as follows:

        First and foremost, candidates with allowed (matching) hashes are
        always preferred over candidates without matching hashes. This is
        because e.g. if the only candidate with an allowed hash is yanked,
        we still want to use that candidate.

        Second, excepting hash considerations, candidates that have been
        yanked (in the sense of PEP 592) are always less preferred than
        candidates that haven't been yanked. Then:

        If not finding wheels, they are sorted by version only.
        If finding wheels, then the sort order is by version, then:
          1. existing installs
          2. wheels ordered via Wheel.support_index_min(self._supported_tags)
          3. source archives
        If prefer_binary was set, then all wheels are sorted above sources.

        Note: it was considered to embed this logic into the Link
              comparison operators, but then different sdist links
              with the same version, would have to be considered equal
        """
        valid_tags = self._supported_tags
        support_num = len(valid_tags)
        build_tag = ()  # type: BuildTag
        binary_preference = 0
        link = candidate.link
        if link.is_wheel:
            # can raise InvalidWheelFilename
            wheel = Wheel(link.filename)
            if not wheel.supported(valid_tags):
                raise UnsupportedWheel(
                    "{} is not a supported wheel for this platform. It "
                    "can't be sorted.".format(wheel.filename)
                )
            if self._prefer_binary:
                binary_preference = 1
            pri = -(wheel.support_index_min(valid_tags))
            if wheel.build_tag is not None:
                match = re.match(r'^(\d+)(.*)$', wheel.build_tag)
                build_tag_groups = match.groups()
                build_tag = (int(build_tag_groups[0]), build_tag_groups[1])
        else:  # sdist
            pri = -(support_num)
        has_allowed_hash = int(link.is_hash_allowed(self._hashes))
        yank_value = -1 * int(link.is_yanked)  # -1 for yanked.
        return (
            has_allowed_hash, yank_value, binary_preference, candidate.version,
            build_tag, pri,
        )
Beispiel #9
0
    def get(
            self,
            link,  # type: Link
            package_name,  # type: Optional[str]
            supported_tags,  # type: List[Tag]
    ):

        # type: (...) -> Link

        candidates = []

        if not package_name:

            return link

        canonical_package_name = canonicalize_name(package_name)

        for wheel_name, wheel_dir in self._get_candidates(
                link, canonical_package_name):

            try:

                wheel = Wheel(wheel_name)

            except InvalidWheelFilename:

                continue

            if canonicalize_name(wheel.name) != canonical_package_name:

                logger.debug(
                    "Ignoring cached wheel %s for %s as it "
                    "does not match the expected distribution name %s.",
                    wheel_name,
                    link,
                    package_name,
                )

                continue

            if not wheel.supported(supported_tags):

                # Built for a different python/arch/etc

                continue

            candidates.append((
                wheel.support_index_min(supported_tags),
                wheel_name,
                wheel_dir,
            ))

        if not candidates:

            return link

        _, wheel_name, wheel_dir = min(candidates)

        return Link(path_to_url(os.path.join(wheel_dir, wheel_name)))
Beispiel #10
0
 def _fail_if_link_is_unsupported_wheel(self, link: Link) -> None:
     if not link.is_wheel:
         return
     wheel = Wheel(link.filename)
     if wheel.supported(self._finder.target_python.get_tags()):
         return
     msg = f"{link.filename} is not a supported wheel on this platform."
     raise UnsupportedWheel(msg)
Beispiel #11
0
 def test_not_supported_osx_version(self):
     """
     Wheels built for macOS 10.9 are not supported on 10.6
     """
     tags = pep425tags.get_supported('27',
                                     platform='macosx_10_6_intel',
                                     impl='cp')
     w = Wheel('simple-0.1-cp27-none-macosx_10_9_intel.whl')
     assert not w.supported(tags=tags)
Beispiel #12
0
    def test_supported_multiarch_darwin(self):
        """
        Multi-arch wheels (intel) are supported on components (i386, x86_64)
        """
        universal = pep425tags.get_supported('27',
                                             platform='macosx_10_5_universal',
                                             impl='cp')
        intel = pep425tags.get_supported('27',
                                         platform='macosx_10_5_intel',
                                         impl='cp')
        x64 = pep425tags.get_supported('27',
                                       platform='macosx_10_5_x86_64',
                                       impl='cp')
        i386 = pep425tags.get_supported('27',
                                        platform='macosx_10_5_i386',
                                        impl='cp')
        ppc = pep425tags.get_supported('27',
                                       platform='macosx_10_5_ppc',
                                       impl='cp')
        ppc64 = pep425tags.get_supported('27',
                                         platform='macosx_10_5_ppc64',
                                         impl='cp')

        w = Wheel('simple-0.1-cp27-none-macosx_10_5_intel.whl')
        assert w.supported(tags=intel)
        assert w.supported(tags=x64)
        assert w.supported(tags=i386)
        assert not w.supported(tags=universal)
        assert not w.supported(tags=ppc)
        assert not w.supported(tags=ppc64)
        w = Wheel('simple-0.1-cp27-none-macosx_10_5_universal.whl')
        assert w.supported(tags=universal)
        assert w.supported(tags=intel)
        assert w.supported(tags=x64)
        assert w.supported(tags=i386)
        assert w.supported(tags=ppc)
        assert w.supported(tags=ppc64)
Beispiel #13
0
 def test_not_supported_version(self):
     """
     Test unsupported wheel is known to be unsupported
     """
     w = Wheel('simple-0.1-py2-none-any.whl')
     assert not w.supported(tags=[Tag('py1', 'none', 'any')])
Beispiel #14
0
 def test_supported_multi_version(self):
     """
     Test multi-version wheel is known to be supported
     """
     w = Wheel('simple-0.1-py2.py3-none-any.whl')
     assert w.supported(tags=[Tag('py3', 'none', 'any')])
Beispiel #15
0
 def test_supported_single_version(self):
     """
     Test single-version wheel is known to be supported
     """
     w = Wheel('simple-0.1-py2-none-any.whl')
     assert w.supported(tags=[Tag('py2', 'none', 'any')])
Beispiel #16
0
 def test_supported_multi_version(self) -> None:
     """
     Test multi-version wheel is known to be supported
     """
     w = Wheel("simple-0.1-py2.py3-none-any.whl")
     assert w.supported(tags=[Tag("py3", "none", "any")])
    def evaluate_link(self, link):
        # type: (Link) -> Tuple[bool, Optional[Text]]
        """
        Determine whether a link is a candidate for installation.

        :return: A tuple (is_candidate, result), where `result` is (1) a
            version string if `is_candidate` is True, and (2) if
            `is_candidate` is False, an optional string to log the reason
            the link fails to qualify.
        """
        version = None
        if link.is_yanked and not self._allow_yanked:
            reason = link.yanked_reason or "<none given>"
            # Mark this as a unicode string to prevent "UnicodeEncodeError:
            # 'ascii' codec can't encode character" in Python 2 when
            # the reason contains non-ascii characters.
            return (False, u"yanked for reason: {}".format(reason))

        if link.egg_fragment:
            egg_info = link.egg_fragment
            ext = link.ext
        else:
            egg_info, ext = link.splitext()
            if not ext:
                return (False, "not a file")
            if ext not in SUPPORTED_EXTENSIONS:
                return (False, "unsupported archive format: {}".format(ext))
            if "binary" not in self._formats and ext == WHEEL_EXTENSION:
                reason = "No binaries permitted for {}".format(
                    self.project_name)
                return (False, reason)
            if "macosx10" in link.path and ext == ".zip":
                return (False, "macosx10 one")
            if ext == WHEEL_EXTENSION:
                try:
                    wheel = Wheel(link.filename)
                except InvalidWheelFilename:
                    return (False, "invalid wheel filename")
                if canonicalize_name(wheel.name) != self._canonical_name:
                    reason = "wrong project name (not {})".format(
                        self.project_name)
                    return (False, reason)

                supported_tags = self._target_python.get_tags()
                if not wheel.supported(supported_tags):
                    # Include the wheel's tags in the reason string to
                    # simplify troubleshooting compatibility issues.
                    file_tags = wheel.get_formatted_file_tags()
                    reason = "none of the wheel's tags match: {}".format(
                        ", ".join(file_tags))
                    return (False, reason)

                version = wheel.version

        # This should be up by the self.ok_binary check, but see issue 2700.
        if "source" not in self._formats and ext != WHEEL_EXTENSION:
            reason = "No sources permitted for {}".format(self.project_name)
            return (False, reason)

        if not version:
            version = _extract_version_from_fragment(
                egg_info,
                self._canonical_name,
            )
        if not version:
            reason = "Missing project version for {}".format(self.project_name)
            return (False, reason)

        match = self._py_version_re.search(version)
        if match:
            version = version[:match.start()]
            py_version = match.group(1)
            if py_version != self._target_python.py_version:
                return (False, "Python version is incorrect")

        supports_python = _check_link_requires_python(
            link,
            version_info=self._target_python.py_version_info,
            ignore_requires_python=self._ignore_requires_python,
        )
        if not supports_python:
            # Return None for the reason text to suppress calling
            # _log_skipped_link().
            return (False, None)

        logger.debug("Found link %s, version: %s", link, version)

        return (True, version)
Beispiel #18
0
    def find_candidates(
        self,
        identifier: str,
        requirements: Mapping[str, Iterator[Requirement]],
        incompatibilities: Mapping[str, Iterator[Candidate]],
        constraint: Constraint,
        prefers_installed: bool,
    ) -> Iterable[Candidate]:

        # Since we cache all the candidates, incompatibility identification
        # can be made quicker by comparing only the id() values.
        incompat_ids = {id(c) for c in incompatibilities.get(identifier, ())}

        explicit_candidates = set()  # type: Set[Candidate]
        ireqs = []  # type: List[InstallRequirement]
        for req in requirements[identifier]:
            cand, ireq = req.get_candidate_lookup()
            if cand is not None and id(cand) not in incompat_ids:
                explicit_candidates.add(cand)
            if ireq is not None:
                ireqs.append(ireq)

        for link in constraint.links:
            if not ireqs:
                # If we hit this condition, then we cannot construct a candidate.
                # However, if we hit this condition, then none of the requirements
                # provided an ireq, so they must have provided an explicit candidate.
                # In that case, either the candidate matches, in which case this loop
                # doesn't need to do anything, or it doesn't, in which case there's
                # nothing this loop can do to recover.
                break
            if link.is_wheel:
                wheel = Wheel(link.filename)
                # Check whether the provided wheel is compatible with the target
                # platform.
                if not wheel.supported(self._finder.target_python.get_tags()):
                    # We are constrained to install a wheel that is incompatible with
                    # the target architecture, so there are no valid candidates.
                    # Return early, with no candidates.
                    return ()
            # Create a "fake" InstallRequirement that's basically a clone of
            # what "should" be the template, but with original_link set to link.
            # Using the given requirement is necessary for preserving hash
            # requirements, but without the original_link, direct_url.json
            # won't be created.
            ireq = install_req_from_link_and_ireq(link, ireqs[0])
            candidate = self._make_candidate_from_link(
                link,
                extras=frozenset(),
                template=ireq,
                name=canonicalize_name(ireq.name) if ireq.name else None,
                version=None,
            )
            if candidate is None:
                # _make_candidate_from_link returns None if the wheel fails to build.
                # We are constrained to install this wheel, so there are no valid
                # candidates.
                # Return early, with no candidates.
                return ()

            explicit_candidates.add(candidate)

        # If none of the requirements want an explicit candidate, we can ask
        # the finder for candidates.
        if not explicit_candidates:
            return self._iter_found_candidates(
                ireqs,
                constraint.specifier,
                constraint.hashes,
                prefers_installed,
                incompat_ids,
            )

        return (c for c in explicit_candidates
                if constraint.is_satisfied_by(c) and all(
                    req.is_satisfied_by(c)
                    for req in requirements[identifier]))
Beispiel #19
0
 def test_supported_single_version(self) -> None:
     """
     Test single-version wheel is known to be supported
     """
     w = Wheel("simple-0.1-py2-none-any.whl")
     assert w.supported(tags=[Tag("py2", "none", "any")])
Beispiel #20
0
    def evaluate_link(self, link: Link) -> Tuple[LinkType, str]:
        """
        Determine whether a link is a candidate for installation.

        :return: A tuple (result, detail), where *result* is an enum
            representing whether the evaluation found a candidate, or the reason
            why one is not found. If a candidate is found, *detail* will be the
            candidate's version string; if one is not found, it contains the
            reason the link fails to qualify.
        """
        version = None
        if link.is_yanked and not self._allow_yanked:
            reason = link.yanked_reason or "<none given>"
            return (LinkType.yanked, f"yanked for reason: {reason}")

        if link.egg_fragment:
            egg_info = link.egg_fragment
            ext = link.ext
        else:
            egg_info, ext = link.splitext()
            if not ext:
                return (LinkType.format_unsupported, "not a file")
            if ext not in SUPPORTED_EXTENSIONS:
                return (
                    LinkType.format_unsupported,
                    f"unsupported archive format: {ext}",
                )
            if "binary" not in self._formats and ext == WHEEL_EXTENSION:
                reason = f"No binaries permitted for {self.project_name}"
                return (LinkType.format_unsupported, reason)
            if "macosx10" in link.path and ext == ".zip":
                return (LinkType.format_unsupported, "macosx10 one")
            if ext == WHEEL_EXTENSION:
                try:
                    wheel = Wheel(link.filename)
                except InvalidWheelFilename:
                    return (
                        LinkType.format_invalid,
                        "invalid wheel filename",
                    )
                if canonicalize_name(wheel.name) != self._canonical_name:
                    reason = f"wrong project name (not {self.project_name})"
                    return (LinkType.different_project, reason)

                supported_tags = self._target_python.get_tags()
                if not wheel.supported(supported_tags):
                    # Include the wheel's tags in the reason string to
                    # simplify troubleshooting compatibility issues.
                    file_tags = ", ".join(wheel.get_formatted_file_tags())
                    reason = (
                        f"none of the wheel's tags ({file_tags}) are compatible "
                        f"(run pip debug --verbose to show compatible tags)")
                    return (LinkType.platform_mismatch, reason)

                version = wheel.version

        # This should be up by the self.ok_binary check, but see issue 2700.
        if "source" not in self._formats and ext != WHEEL_EXTENSION:
            reason = f"No sources permitted for {self.project_name}"
            return (LinkType.format_unsupported, reason)

        if not version:
            version = _extract_version_from_fragment(
                egg_info,
                self._canonical_name,
            )
        if not version:
            reason = f"Missing project version for {self.project_name}"
            return (LinkType.format_invalid, reason)

        match = self._py_version_re.search(version)
        if match:
            version = version[:match.start()]
            py_version = match.group(1)
            if py_version != self._target_python.py_version:
                return (
                    LinkType.platform_mismatch,
                    "Python version is incorrect",
                )

        supports_python = _check_link_requires_python(
            link,
            version_info=self._target_python.py_version_info,
            ignore_requires_python=self._ignore_requires_python,
        )
        if not supports_python:
            reason = f"{version} Requires-Python {link.requires_python}"
            return (LinkType.requires_python_mismatch, reason)

        logger.debug("Found link %s, version: %s", link, version)

        return (LinkType.candidate, version)
Beispiel #21
0
    def test_supported_multiarch_darwin(self) -> None:
        """
        Multi-arch wheels (intel) are supported on components (i386, x86_64)
        """
        universal = compatibility_tags.get_supported(
            "27", platforms=["macosx_10_5_universal"], impl="cp")
        intel = compatibility_tags.get_supported(
            "27", platforms=["macosx_10_5_intel"], impl="cp")
        x64 = compatibility_tags.get_supported(
            "27", platforms=["macosx_10_5_x86_64"], impl="cp")
        i386 = compatibility_tags.get_supported("27",
                                                platforms=["macosx_10_5_i386"],
                                                impl="cp")
        ppc = compatibility_tags.get_supported("27",
                                               platforms=["macosx_10_5_ppc"],
                                               impl="cp")
        ppc64 = compatibility_tags.get_supported(
            "27", platforms=["macosx_10_5_ppc64"], impl="cp")

        w = Wheel("simple-0.1-cp27-none-macosx_10_5_intel.whl")
        assert w.supported(tags=intel)
        assert w.supported(tags=x64)
        assert w.supported(tags=i386)
        assert not w.supported(tags=universal)
        assert not w.supported(tags=ppc)
        assert not w.supported(tags=ppc64)
        w = Wheel("simple-0.1-cp27-none-macosx_10_5_universal.whl")
        assert w.supported(tags=universal)
        assert w.supported(tags=intel)
        assert w.supported(tags=x64)
        assert w.supported(tags=i386)
        assert w.supported(tags=ppc)
        assert w.supported(tags=ppc64)
Beispiel #22
0
    def evaluate_link(self, link):
        # type: (Link) -> Tuple[bool, Optional[str]]
        """
        Determine whether a link is a candidate for installation.

        :return: A tuple (is_candidate, result), where `result` is (1) a
            version string if `is_candidate` is True, and (2) if
            `is_candidate` is False, an optional string to log the reason
            the link fails to qualify.
        """
        version = None
        if link.is_yanked and not self._allow_yanked:
            reason = link.yanked_reason or '<none given>'
            return (False, f'yanked for reason: {reason}')

        if link.egg_fragment:
            egg_info = link.egg_fragment
            ext = link.ext
        else:
            egg_info, ext = link.splitext()
            if not ext:
                return (False, 'not a file')
            if ext not in SUPPORTED_EXTENSIONS:
                return (False, f'unsupported archive format: {ext}')
            if "binary" not in self._formats and ext == WHEEL_EXTENSION:
                reason = 'No binaries permitted for {}'.format(
                    self.project_name)
                return (False, reason)
            if "macosx10" in link.path and ext == '.zip':
                return (False, 'macosx10 one')
            if ext == WHEEL_EXTENSION:
                try:
                    wheel = Wheel(link.filename)
                except InvalidWheelFilename:
                    return (False, 'invalid wheel filename')
                if canonicalize_name(wheel.name) != self._canonical_name:
                    reason = 'wrong project name (not {})'.format(
                        self.project_name)
                    return (False, reason)

                supported_tags = self._target_python.get_tags()
                if not wheel.supported(supported_tags):
                    # Include the wheel's tags in the reason string to
                    # simplify troubleshooting compatibility issues.
                    file_tags = wheel.get_formatted_file_tags()
                    reason = ("none of the wheel's tags match: {}".format(
                        ', '.join(file_tags)))
                    return (False, reason)

                version = wheel.version

        # This should be up by the self.ok_binary check, but see issue 2700.
        if "source" not in self._formats and ext != WHEEL_EXTENSION:
            reason = f'No sources permitted for {self.project_name}'
            return (False, reason)

        if not version:
            version = _extract_version_from_fragment(
                egg_info,
                self._canonical_name,
            )
        if not version:
            reason = f'Missing project version for {self.project_name}'
            return (False, reason)

        match = self._py_version_re.search(version)
        if match:
            version = version[:match.start()]
            py_version = match.group(1)
            if py_version != self._target_python.py_version:
                return (False, 'Python version is incorrect')

        supports_python = _check_link_requires_python(
            link,
            version_info=self._target_python.py_version_info,
            ignore_requires_python=self._ignore_requires_python,
        )
        if not supports_python:
            # Return None for the reason text to suppress calling
            # _log_skipped_link().
            return (False, None)

        logger.debug('Found link %s, version: %s', link, version)

        return (True, version)
Beispiel #23
0
    def add_requirement(
        self,
        install_req,  # type: InstallRequirement
        parent_req_name=None,  # type: Optional[str]
        extras_requested=None,  # type: Optional[Iterable[str]]
    ):
        # type: (...) -> Tuple[List[InstallRequirement], Optional[InstallRequirement]]  # noqa: E501
        """Add install_req as a requirement to install.

        :param parent_req_name: The name of the requirement that needed this
            added. The name is used because when multiple unnamed requirements
            resolve to the same name, we could otherwise end up with dependency
            links that point outside the Requirements set. parent_req must
            already be added. Note that None implies that this is a user
            supplied requirement, vs an inferred one.
        :param extras_requested: an iterable of extras used to evaluate the
            environment markers.
        :return: Additional requirements to scan. That is either [] if
            the requirement is not applicable, or [install_req] if the
            requirement is applicable and has just been added.
        """
        # If the markers do not match, ignore this requirement.
        if not install_req.match_markers(extras_requested):
            logger.info(
                "Ignoring %s: markers '%s' don't match your environment",
                install_req.name,
                install_req.markers,
            )
            return [], None

        # If the wheel is not supported, raise an error.
        # Should check this after filtering out based on environment markers to
        # allow specifying different wheels based on the environment/OS, in a
        # single requirements file.
        if install_req.link and install_req.link.is_wheel:
            wheel = Wheel(install_req.link.filename)
            tags = pep425tags.get_supported()
            if self.check_supported_wheels and not wheel.supported(tags):
                raise InstallationError(
                    "%s is not a supported wheel on this platform." % wheel.filename
                )

        # This next bit is really a sanity check.
        assert install_req.is_direct == (parent_req_name is None), (
            "a direct req shouldn't have a parent and also, "
            "a non direct req should have a parent"
        )

        # Unnamed requirements are scanned again and the requirement won't be
        # added as a dependency until after scanning.
        if not install_req.name:
            self.add_unnamed_requirement(install_req)
            return [install_req], None

        try:
            existing_req = self.get_requirement(install_req.name)
        except KeyError:
            existing_req = None

        has_conflicting_requirement = (
            parent_req_name is None
            and existing_req
            and not existing_req.constraint
            and existing_req.extras == install_req.extras
            and existing_req.req.specifier != install_req.req.specifier
        )
        if has_conflicting_requirement:
            raise InstallationError(
                "Double requirement given: %s (already in %s, name=%r)"
                % (install_req, existing_req, install_req.name)
            )

        # When no existing requirement exists, add the requirement as a
        # dependency and it will be scanned again after.
        if not existing_req:
            self.add_named_requirement(install_req)
            # We'd want to rescan this requirement later
            return [install_req], install_req

        # Assume there's no need to scan, and that we've already
        # encountered this for scanning.
        if install_req.constraint or not existing_req.constraint:
            return [], existing_req

        does_not_satisfy_constraint = install_req.link and not (
            existing_req.link and install_req.link.path == existing_req.link.path
        )
        if does_not_satisfy_constraint:
            self.reqs_to_cleanup.append(install_req)
            raise InstallationError(
                "Could not satisfy constraints for '%s': "
                "installation from path or url cannot be "
                "constrained to a version" % install_req.name,
            )
        # If we're now installing a constraint, mark the existing
        # object for real installation.
        existing_req.constraint = False
        existing_req.extras = tuple(
            sorted(set(existing_req.extras) | set(install_req.extras))
        )
        logger.debug(
            "Setting %s extras to: %s",
            existing_req,
            existing_req.extras,
        )
        # Return the existing requirement for addition to the parent and
        # scanning again.
        return [existing_req], existing_req
Beispiel #24
0
 def test_not_supported_version(self) -> None:
     """
     Test unsupported wheel is known to be unsupported
     """
     w = Wheel("simple-0.1-py2-none-any.whl")
     assert not w.supported(tags=[Tag("py1", "none", "any")])