Esempio n. 1
0
    def find_best_match(self, ireq, prereleases=None):
        """
        Returns a Version object that indicates the best match for the given
        InstallRequirement according to the external repository.
        """
        if ireq.editable:
            return ireq  # return itself as the best match

        py_version = parse_version(
            os.environ.get('PIP_PYTHON_VERSION', str(sys.version_info[:3])))
        all_candidates = []
        for c in self.find_all_candidates(ireq.name):
            if c.requires_python:
                # Old specifications had people setting this to single digits
                # which is effectively the same as '>=digit,<digit+1'
                if c.requires_python.isdigit():
                    c.requires_python = '>={0},<{1}'.format(
                        c.requires_python,
                        int(c.requires_python) + 1)
                try:
                    specifier_set = SpecifierSet(c.requires_python)
                except InvalidSpecifier:
                    pass
                else:
                    if not specifier_set.contains(py_version):
                        continue
            all_candidates.append(c)

        candidates_by_version = lookup_table(all_candidates,
                                             key=lambda c: c.version,
                                             unique=True)
        try:
            matching_versions = ireq.specifier.filter(
                (candidate.version for candidate in all_candidates),
                prereleases=prereleases)
        except TypeError:
            matching_versions = [
                candidate.version for candidate in all_candidates
            ]

        # Reuses pip's internal candidate sort key to sort
        matching_candidates = [
            candidates_by_version[ver] for ver in matching_versions
        ]
        if not matching_candidates:
            raise NoCandidateFound(ireq, all_candidates, self.finder)
        best_candidate = max(matching_candidates,
                             key=self.finder._candidate_sort_key)

        # Turn the candidate into a pinned InstallRequirement
        new_req = make_install_requirement(best_candidate.project,
                                           best_candidate.version,
                                           ireq.extras,
                                           ireq.markers,
                                           constraint=ireq.constraint)

        # KR TODO: Marker here?

        return new_req
Esempio n. 2
0
    def requires_python(self) -> SpecifierSet:
        """Value of "Requires-Python:" in distribution metadata.

        If the key does not exist or contains an invalid value, an empty
        SpecifierSet should be returned.
        """
        value = self.metadata.get("Requires-Python")
        if value is None:
            return SpecifierSet()
        try:
            # Convert to str to satisfy the type checker; this can be a Header object.
            spec = SpecifierSet(str(value))
        except InvalidSpecifier as e:
            message = "Package %r has an invalid Requires-Python: %s"
            logger.warning(message, self.raw_name, e)
            return SpecifierSet()
        return spec
Esempio n. 3
0
def clean_requires_python(candidates):
    """Get a cleaned list of all the candidates with valid specifiers in the `requires_python` attributes."""
    all_candidates = []
    py_version = parse_version(os.environ.get('PIP_PYTHON_VERSION', '.'.join(map(str, sys.version_info[:3]))))
    for c in candidates:
        if c.requires_python:
            # Old specifications had people setting this to single digits
            # which is effectively the same as '>=digit,<digit+1'
            if c.requires_python.isdigit():
                c.requires_python = '>={0},<{1}'.format(c.requires_python, int(c.requires_python) + 1)
            try:
                specifierset = SpecifierSet(c.requires_python)
            except InvalidSpecifier:
                continue
            else:
                if not specifierset.contains(py_version):
                    continue
        all_candidates.append(c)
    return all_candidates
Esempio n. 4
0
def clean_requires_python(candidates):
    """Get a cleaned list of all the candidates with valid specifiers in the `requires_python` attributes."""
    all_candidates = []
    py_version = parse_version(os.environ.get('PIP_PYTHON_VERSION', '.'.join(map(str, sys.version_info[:3]))))
    for c in candidates:
        if c.requires_python:
            # Old specifications had people setting this to single digits
            # which is effectively the same as '>=digit,<digit+1'
            if c.requires_python.isdigit():
                c.requires_python = '>={0},<{1}'.format(c.requires_python, int(c.requires_python) + 1)
            try:
                specifierset = SpecifierSet(c.requires_python)
            except InvalidSpecifier:
                continue
            else:
                if not specifierset.contains(py_version):
                    continue
        all_candidates.append(c)
    return all_candidates
Esempio n. 5
0
 def _get_requires_python_dependency(self) -> Optional[Requirement]:
     requires_python = get_requires_python(self.dist)
     if requires_python is None:
         return None
     try:
         spec = SpecifierSet(requires_python)
     except InvalidSpecifier as e:
         message = "Package %r has an invalid Requires-Python: %s"
         logger.warning(message, self.name, e)
         return None
     return self._factory.make_requires_python_requirement(spec)
Esempio n. 6
0
 def get_install_requirement(self) -> Optional[InstallRequirement]:
     ireq = self._ireq
     if self._version and ireq.req and not ireq.req.url:
         ireq.req.specifier = SpecifierSet(f"=={self._version}")
     return ireq
Esempio n. 7
0
    def get_legacy_dependencies(self, ireq):
        """
        Given a pinned or an editable InstallRequirement, returns a set of
        dependencies (also InstallRequirements, but not necessarily pinned).
        They indicate the secondary dependencies for the given requirement.
        """
        if not (ireq.editable or is_pinned_requirement(ireq)):
            raise TypeError(
                'Expected pinned or editable InstallRequirement, got {}'.
                format(ireq))

        if ireq not in self._dependencies_cache:
            if ireq.editable and (ireq.source_dir
                                  and os.path.exists(ireq.source_dir)):
                # No download_dir for locally available editable requirements.
                # If a download_dir is passed, pip will  unnecessarely
                # archive the entire source directory
                download_dir = None
            elif ireq.link and not ireq.link.is_artifact:
                # No download_dir for VCS sources.  This also works around pip
                # using git-checkout-index, which gets rid of the .git dir.
                download_dir = None
            else:
                download_dir = self._download_dir
                if not os.path.isdir(download_dir):
                    os.makedirs(download_dir)
            if not os.path.isdir(self._wheel_download_dir):
                os.makedirs(self._wheel_download_dir)
            # Collect setup_requires info from local eggs.
            # Do this after we call the preparer on these reqs to make sure their
            # egg info has been created
            setup_requires = {}
            dist = None
            if ireq.editable:
                try:
                    dist = ireq.get_dist()
                except InstallationError:
                    ireq.run_egg_info()
                    dist = ireq.get_dist()
                except (TypeError, ValueError, AttributeError):
                    pass
                else:
                    if dist.has_metadata('requires.txt'):
                        setup_requires = self.finder.get_extras_links(
                            dist.get_metadata_lines('requires.txt'))
            try:
                # Pip 9 and below
                reqset = RequirementSet(
                    self.build_dir,
                    self.source_dir,
                    download_dir=download_dir,
                    wheel_download_dir=self._wheel_download_dir,
                    session=self.session,
                    ignore_installed=True,
                    ignore_compatibility=False,
                    wheel_cache=self.wheel_cache,
                )
                result = reqset._prepare_file(self.finder,
                                              ireq,
                                              ignore_requires_python=True)
            except TypeError:
                # Pip >= 10 (new resolver!)
                preparer = RequirementPreparer(
                    build_dir=self.build_dir,
                    src_dir=self.source_dir,
                    download_dir=download_dir,
                    wheel_download_dir=self._wheel_download_dir,
                    progress_bar='off',
                    build_isolation=False)
                reqset = RequirementSet()
                ireq.is_direct = True
                reqset.add_requirement(ireq)
                self.resolver = PipResolver(preparer=preparer,
                                            finder=self.finder,
                                            session=self.session,
                                            upgrade_strategy="to-satisfy-only",
                                            force_reinstall=True,
                                            ignore_dependencies=False,
                                            ignore_requires_python=True,
                                            ignore_installed=True,
                                            isolated=False,
                                            wheel_cache=self.wheel_cache,
                                            use_user_site=False,
                                            ignore_compatibility=False)
                self.resolver.resolve(reqset)
                result = set(reqset.requirements.values())

            # HACK: Sometimes the InstallRequirement doesn't properly get
            # these values set on it during the resolution process. It's
            # difficult to pin down what is going wrong. This fixes things.
            if not getattr(ireq, 'version', None):
                try:
                    dist = ireq.get_dist() if not dist else None
                    ireq.version = ireq.get_dist().version
                except (ValueError, OSError, TypeError, AttributeError) as e:
                    pass
            if not getattr(ireq, 'project_name', None):
                try:
                    ireq.project_name = dist.project_name if dist else None
                except (ValueError, TypeError) as e:
                    pass
            if not getattr(ireq, 'req', None):
                try:
                    ireq.req = dist.as_requirement() if dist else None
                except (ValueError, TypeError) as e:
                    pass

            # Convert setup_requires dict into a somewhat usable form.
            if setup_requires:
                for section in setup_requires:
                    python_version = section
                    not_python = not (section.startswith('[')
                                      and ':' in section)

                    # This is for cleaning up :extras: formatted markers
                    # by adding them to the results of the resolver
                    # since any such extra would have been returned as a result anyway
                    for value in setup_requires[section]:
                        # This is a marker.
                        if value.startswith('[') and ':' in value:
                            python_version = value[1:-1]
                            not_python = False
                        # Strip out other extras.
                        if value.startswith('[') and ':' not in value:
                            not_python = True

                        if ':' not in value:
                            try:
                                if not not_python:
                                    result = result + [
                                        InstallRequirement.from_line(
                                            "{0}{1}".format(
                                                value, python_version).replace(
                                                    ':', ';'))
                                    ]
                            # Anything could go wrong here -- can't be too careful.
                            except Exception:
                                pass

            # this section properly creates 'python_version' markers for cross-python
            # virtualenv creation and for multi-python compatibility.
            requires_python = reqset.requires_python if hasattr(
                reqset, 'requires_python') else self.resolver.requires_python
            if requires_python:
                marker_str = ''
                # This corrects a logic error from the previous code which said that if
                # we Encountered any 'requires_python' attributes, basically only create a
                # single result no matter how many we resolved.  This should fix
                # a majority of the remaining non-deterministic resolution issues.
                if any(
                        requires_python.startswith(op)
                        for op in Specifier._operators.keys()):
                    # We are checking first if we have  leading specifier operator
                    # if not, we can assume we should be doing a == comparison
                    specifierset = list(SpecifierSet(requires_python))
                    # for multiple specifiers, the correct way to represent that in
                    # a specifierset is `Requirement('fakepkg; python_version<"3.0,>=2.6"')`
                    marker_key = Variable('python_version')
                    markers = []
                    for spec in specifierset:
                        operator, val = spec._spec
                        operator = Op(operator)
                        val = Value(val)
                        markers.append(''.join([
                            marker_key.serialize(),
                            operator.serialize(),
                            val.serialize()
                        ]))
                    marker_str = ' and '.join(markers)
                # The best way to add markers to a requirement is to make a separate requirement
                # with only markers on it, and then to transfer the object istelf
                marker_to_add = Requirement(
                    'fakepkg; {0}'.format(marker_str)).marker
                result.remove(ireq)
                ireq.req.marker = marker_to_add
                result.add(ireq)

            self._dependencies_cache[ireq] = result
            reqset.cleanup_files()

        return set(self._dependencies_cache[ireq])
Esempio n. 8
0
 def empty(cls) -> "Constraint":
     return Constraint(SpecifierSet(), Hashes(), frozenset())