Esempio n. 1
0
    def _prepare(self):
        # type: () -> None
        if self._dist is not None:
            return

        abstract_dist = self._prepare_abstract_distribution()
        self._dist = abstract_dist.get_pkg_resources_distribution()
        assert self._dist is not None, "Distribution already installed"

        # TODO: Abort cleanly here, as the resolution has been
        #       based on the wrong name/version until now, and
        #       so is wrong.
        # TODO: (Longer term) Rather than abort, reject this candidate
        #       and backtrack. This would need resolvelib support.
        # These should be "proper" errors, not just asserts, as they
        # can result from user errors like a requirement "foo @ URL"
        # when the project at URL has a name of "bar" in its metadata.
        assert (self._name is None or self._name == canonicalize_name(
            self._dist.project_name)), "Name mismatch: {!r} vs {!r}".format(
                self._name,
                canonicalize_name(self._dist.project_name),
            )
        assert (self._version is None
                or self._version == self._dist.parsed_version
                ), "Version mismatch: {!r} vs {!r}".format(
                    self._version,
                    self._dist.parsed_version,
                )
Esempio n. 2
0
def wheel_dist_info_dir(source, name):
    # type: (ZipFile, str) -> str
    """Returns the name of the contained .dist-info directory.

    Raises AssertionError or UnsupportedWheel if not found, >1 found, or
    it doesn't match the provided name.
    """
    # Zip file path separators must be /
    subdirs = list(set(p.split("/")[0] for p in source.namelist()))

    info_dirs = [s for s in subdirs if s.endswith('.dist-info')]

    if not info_dirs:
        raise UnsupportedWheel(".dist-info directory not found")

    if len(info_dirs) > 1:
        raise UnsupportedWheel(
            "multiple .dist-info directories found: {}".format(
                ", ".join(info_dirs)))

    info_dir = info_dirs[0]

    info_dir_name = canonicalize_name(info_dir)
    canonical_name = canonicalize_name(name)
    if not info_dir_name.startswith(canonical_name):
        raise UnsupportedWheel(
            ".dist-info directory {!r} does not start with {!r}".format(
                info_dir, canonical_name))

    # Zip file paths can be unicode or str depending on the zip entry flags,
    # so normalize it.
    return ensure_str(info_dir)
Esempio n. 3
0
    def run(self, options, args):
        session = self.get_default_session(options)

        reqs_to_uninstall = {}
        for name in args:
            req = install_req_from_line(
                name,
                isolated=options.isolated_mode,
            )
            if req.name:
                reqs_to_uninstall[canonicalize_name(req.name)] = req
        for filename in options.requirements:
            for parsed_req in parse_requirements(filename,
                                                 options=options,
                                                 session=session):
                req = install_req_from_parsed_requirement(
                    parsed_req, isolated=options.isolated_mode)
                if req.name:
                    reqs_to_uninstall[canonicalize_name(req.name)] = req
        if not reqs_to_uninstall:
            raise InstallationError(
                'You must give at least one requirement to {self.name} (see '
                '"pip help {self.name}")'.format(**locals()))

        protect_pip_from_modification_on_windows(
            modifying_pip="pip" in reqs_to_uninstall)

        for req in reqs_to_uninstall.values():
            uninstall_pathset = req.uninstall(
                auto_confirm=options.yes,
                verbose=self.verbosity > 0,
            )
            if uninstall_pathset:
                uninstall_pathset.commit()
Esempio n. 4
0
 def get_requiring_packages(package_name):
     canonical_name = canonicalize_name(package_name)
     return [
         pkg.project_name for pkg in pkg_resources.working_set
         if canonical_name in
         [canonicalize_name(required.name) for required in pkg.requires()]
     ]
Esempio n. 5
0
    def warn_on_mismatching_name(self):
        # type: () -> None
        metadata_name = canonicalize_name(self.metadata["Name"])
        if canonicalize_name(self.req.name) == metadata_name:
            # Everything is fine.
            return

        # If we're here, there's a mismatch. Log a warning about it.
        logger.warning(
            'Generating metadata for package %s '
            'produced metadata for project name %s. Fix your '
            '#egg=%s fragments.',
            self.name, metadata_name, self.name
        )
        self.req = Requirement(metadata_name)
Esempio n. 6
0
def _find_name_version_sep(fragment, canonical_name):
    # type: (str, str) -> int
    """Find the separator's index based on the package's canonical name.

    :param fragment: A <package>+<version> filename "fragment" (stem) or
        egg fragment.
    :param canonical_name: The package's canonical name.

    This function is needed since the canonicalized name does not necessarily
    have the same length as the egg info's name part. An example::

    >>> fragment = 'foo__bar-1.0'
    >>> canonical_name = 'foo-bar'
    >>> _find_name_version_sep(fragment, canonical_name)
    8
    """
    # Project name and version must be separated by one single dash. Find all
    # occurrences of dashes; if the string in front of it matches the canonical
    # name, this is the one separating the name and version parts.
    for i, c in enumerate(fragment):
        if c != "-":
            continue
        if canonicalize_name(fragment[:i]) == canonical_name:
            return i
    raise ValueError("{} does not match {}".format(fragment, canonical_name))
Esempio n. 7
0
 def check_binary_allowed(req):
     # type: (InstallRequirement) -> bool
     if req.use_pep517:
         return True
     canonical_name = canonicalize_name(req.name)
     allowed_formats = format_control.get_allowed_formats(canonical_name)
     return "binary" in allowed_formats
Esempio n. 8
0
 def __init__(self, name, req, editable, comments=()):
     # type: (str, Union[str, Requirement], bool, Iterable[str]) -> None
     self.name = name
     self.canonical_name = canonicalize_name(name)
     self.req = req
     self.editable = editable
     self.comments = comments
Esempio n. 9
0
    def iter_found_candidates(self, ireq, extras):
        # type: (InstallRequirement, Set[str]) -> Iterator[Candidate]
        name = canonicalize_name(ireq.req.name)
        if not self._force_reinstall:
            installed_dist = self._installed_dists.get(name)
        else:
            installed_dist = None

        found = self.finder.find_best_candidate(
            project_name=ireq.req.name,
            specifier=ireq.req.specifier,
            hashes=ireq.hashes(trust_internet=False),
        )
        for ican in found.iter_applicable():
            if (installed_dist is not None and
                    installed_dist.parsed_version == ican.version):
                continue
            yield self._make_candidate_from_link(
                link=ican.link,
                extras=extras,
                parent=ireq,
                name=name,
                version=ican.version,
            )

        # Return installed distribution if it matches the specifier. This is
        # done last so the resolver will prefer it over downloading links.
        if (installed_dist is not None and
                installed_dist.parsed_version in ireq.req.specifier):
            yield self._make_candidate_from_dist(
                dist=installed_dist,
                extras=extras,
                parent=ireq,
            )
Esempio n. 10
0
    def __init__(
        self,
        finder,  # type: PackageFinder
        preparer,  # type: RequirementPreparer
        make_install_req,  # type: InstallRequirementProvider
        force_reinstall,  # type: bool
        ignore_installed,  # type: bool
        ignore_requires_python,  # type: bool
        py_version_info=None,  # type: Optional[Tuple[int, ...]]
    ):
        # type: (...) -> None
        self.finder = finder
        self.preparer = preparer
        self._python_candidate = RequiresPythonCandidate(py_version_info)
        self._make_install_req_from_spec = make_install_req
        self._force_reinstall = force_reinstall
        self._ignore_requires_python = ignore_requires_python

        self._link_candidate_cache = {}  # type: Cache[LinkCandidate]
        self._editable_candidate_cache = {}  # type: Cache[EditableCandidate]

        if not ignore_installed:
            self._installed_dists = {
                canonicalize_name(dist.project_name): dist
                for dist in get_installed_distributions()
            }
        else:
            self._installed_dists = {}
Esempio n. 11
0
 def __str__(self):
     # type: () -> str
     requirements = sorted(
         (req for req in self.requirements.values() if not req.comes_from),
         key=lambda req: canonicalize_name(req.name),
     )
     return ' '.join(str(req.req) for req in requirements)
Esempio n. 12
0
    def get_requirement(self, name):
        # type: (str) -> InstallRequirement
        project_name = canonicalize_name(name)

        if project_name in self.requirements:
            return self.requirements[project_name]

        raise KeyError("No project with the name {name!r}".format(**locals()))
Esempio n. 13
0
 def mkurl_pypi_url(url):
     # type: (str) -> str
     loc = posixpath.join(
         url, urllib_parse.quote(canonicalize_name(project_name)))
     # For maximum compatibility with easy_install, ensure the path
     # ends in a trailing slash.  Although this isn't in the spec
     # (and PyPI can handle it without the slash) some other index
     # implementations might break if they relied on easy_install's
     # behavior.
     if not loc.endswith('/'):
         loc = loc + '/'
     return loc
Esempio n. 14
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 {} 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,
                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)))
Esempio n. 15
0
def _req_set_item_sorter(
        item,  # type: Tuple[str, InstallRequirement]
        weights,  # type: Dict[Optional[str], int]
):
    # type: (...) -> Tuple[int, str]
    """Key function used to sort install requirements for installation.

    Based on the "weight" mapping calculated in ``get_installation_order()``.
    The canonical package name is returned as the second member as a tie-
    breaker to ensure the result is predictable, which is useful in tests.
    """
    name = canonicalize_name(item[0])
    return weights[name], name
Esempio n. 16
0
    def make_link_evaluator(self, project_name):
        # type: (str) -> LinkEvaluator
        canonical_name = canonicalize_name(project_name)
        formats = self.format_control.get_allowed_formats(canonical_name)

        return LinkEvaluator(
            project_name=project_name,
            canonical_name=canonical_name,
            formats=formats,
            target_python=self._target_python,
            allow_yanked=self._allow_yanked,
            ignore_requires_python=self._ignore_requires_python,
        )
Esempio n. 17
0
    def __repr__(self):
        # type: () -> str
        requirements = sorted(
            self.requirements.values(),
            key=lambda req: canonicalize_name(req.name),
        )

        format_string = '<{classname} object; {count} requirement(s): {reqs}>'
        return format_string.format(
            classname=self.__class__.__name__,
            count=len(requirements),
            reqs=', '.join(str(req.req) for req in requirements),
        )
Esempio n. 18
0
def _create_whitelist(would_be_installed, package_set):
    # type: (Set[str], PackageSet) -> Set[str]
    packages_affected = set(would_be_installed)

    for package_name in package_set:
        if package_name in packages_affected:
            continue

        for req in package_set[package_name].requires:
            if canonicalize_name(req.name) in packages_affected:
                packages_affected.add(package_name)
                break

    return packages_affected
Esempio n. 19
0
def make_install_req_from_dist(dist, parent):
    # type: (Distribution, InstallRequirement) -> InstallRequirement
    ireq = install_req_from_line(
        "{}=={}".format(
            canonicalize_name(dist.project_name),
            dist.parsed_version,
        ),
        comes_from=parent.comes_from,
        use_pep517=parent.use_pep517,
        isolated=parent.isolated,
        constraint=parent.constraint,
        options=dict(install_options=parent.install_options,
                     global_options=parent.global_options,
                     hashes=parent.hash_options),
    )
    ireq.satisfied_by = dist
    return ireq
Esempio n. 20
0
def check_package_set(package_set, should_ignore=None):
    # type: (PackageSet, Optional[Callable[[str], bool]]) -> CheckResult
    """Check if a package set is consistent

    If should_ignore is passed, it should be a callable that takes a
    package name and returns a boolean.
    """
    if should_ignore is None:
        def should_ignore(name):
            return False

    missing = {}
    conflicting = {}

    for package_name in package_set:
        # Info about dependencies of package_name
        missing_deps = set()  # type: Set[Missing]
        conflicting_deps = set()  # type: Set[Conflicting]

        if should_ignore(package_name):
            continue

        for req in package_set[package_name].requires:
            name = canonicalize_name(req.project_name)  # type: str

            # Check if it's missing
            if name not in package_set:
                missed = True
                if req.marker is not None:
                    missed = req.marker.evaluate()
                if missed:
                    missing_deps.add((name, req))
                continue

            # Check if there's a conflict
            version = package_set[name].version  # type: str
            if not req.specifier.contains(version, prereleases=True):
                conflicting_deps.add((name, version, req))

        if missing_deps:
            missing[package_name] = sorted(missing_deps, key=str)
        if conflicting_deps:
            conflicting[package_name] = sorted(conflicting_deps, key=str)

    return missing, conflicting
Esempio n. 21
0
def create_package_set_from_installed(**kwargs):
    # type: (**Any) -> Tuple[PackageSet, bool]
    """Converts a list of distributions into a PackageSet.
    """
    # Default to using all packages installed on the system
    if kwargs == {}:
        kwargs = {"local_only": False, "skip": ()}

    package_set = {}
    problems = False
    for dist in get_installed_distributions(**kwargs):
        name = canonicalize_name(dist.project_name)
        try:
            package_set[name] = PackageDetails(dist.version, dist.requires())
        except RequirementParseError as e:
            # Don't crash on broken metadata
            logger.warning("Error parsing requirements for %s: %s", name, e)
            problems = True
    return package_set, problems
Esempio n. 22
0
def _simulate_installation_of(to_install, package_set):
    # type: (List[InstallRequirement], PackageSet) -> Set[str]
    """Computes the version of packages after installing to_install.
    """

    # Keep track of packages that were installed
    installed = set()

    # Modify it as installing requirement_set would (assuming no errors)
    for inst_req in to_install:
        abstract_dist = make_distribution_for_install_requirement(inst_req)
        dist = abstract_dist.get_pkg_resources_distribution()

        name = canonicalize_name(dist.key)
        package_set[name] = PackageDetails(dist.version, dist.requires())

        installed.add(name)

    return installed
Esempio n. 23
0
 def handle_mutual_excludes(value, target, other):
     # type: (str, Optional[Set[str]], Optional[Set[str]]) -> None
     if value.startswith('-'):
         raise CommandError(
             "--no-binary / --only-binary option requires 1 argument.")
     new = value.split(',')
     while ':all:' in new:
         other.clear()
         target.clear()
         target.add(':all:')
         del new[:new.index(':all:') + 1]
         # Without a none, we want to discard everything as :all: covers it
         if ':none:' not in new:
             return
     for name in new:
         if name == ':none:':
             target.clear()
             continue
         name = canonicalize_name(name)
         other.discard(name)
         target.add(name)
Esempio n. 24
0
    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)
Esempio n. 25
0
 def name(self):
     # type: () -> str
     return canonicalize_name(self.dist.project_name)
Esempio n. 26
0
 def name(self):
     # type: () -> str
     """The normalised name of the project the candidate refers to"""
     if self._name is None:
         self._name = canonicalize_name(self.dist.project_name)
     return self._name
Esempio n. 27
0
    def add_named_requirement(self, install_req):
        # type: (InstallRequirement) -> None
        assert install_req.name

        project_name = canonicalize_name(install_req.name)
        self.requirements[project_name] = install_req
Esempio n. 28
0
    def has_requirement(self, name):
        # type: (str) -> bool
        project_name = canonicalize_name(name)

        return (project_name in self.requirements
                and not self.requirements[project_name].constraint)
Esempio n. 29
0
def format_name(project, extras):
    # type: (str, Set[str]) -> str
    if not extras:
        return project
    canonical_extras = sorted(canonicalize_name(e) for e in extras)
    return "{}[{}]".format(project, ",".join(canonical_extras))
Esempio n. 30
0
def search_packages_info(query):
    """
    Gather details from installed distributions. Print distribution name,
    version, location, and installed files. Installed files requires a
    pip generated 'installed-files.txt' in the distributions '.egg-info'
    directory.
    """
    installed = {}
    for p in pkg_resources.working_set:
        installed[canonicalize_name(p.project_name)] = p

    query_names = [canonicalize_name(name) for name in query]
    missing = sorted([
        name for name, pkg in zip(query, query_names) if pkg not in installed
    ])
    if missing:
        logger.warning('Package(s) not found: %s', ', '.join(missing))

    def get_requiring_packages(package_name):
        canonical_name = canonicalize_name(package_name)
        return [
            pkg.project_name for pkg in pkg_resources.working_set
            if canonical_name in
            [canonicalize_name(required.name) for required in pkg.requires()]
        ]

    for dist in [installed[pkg] for pkg in query_names if pkg in installed]:
        package = {
            'name': dist.project_name,
            'version': dist.version,
            'location': dist.location,
            'requires': [dep.project_name for dep in dist.requires()],
            'required_by': get_requiring_packages(dist.project_name)
        }
        file_list = None
        metadata = None
        if isinstance(dist, pkg_resources.DistInfoDistribution):
            # RECORDs should be part of .dist-info metadatas
            if dist.has_metadata('RECORD'):
                lines = dist.get_metadata_lines('RECORD')
                paths = [l.split(',')[0] for l in lines]
                paths = [os.path.join(dist.location, p) for p in paths]
                file_list = [os.path.relpath(p, dist.location) for p in paths]

            if dist.has_metadata('METADATA'):
                metadata = dist.get_metadata('METADATA')
        else:
            # Otherwise use pip's log for .egg-info's
            if dist.has_metadata('installed-files.txt'):
                paths = dist.get_metadata_lines('installed-files.txt')
                paths = [os.path.join(dist.egg_info, p) for p in paths]
                file_list = [os.path.relpath(p, dist.location) for p in paths]

            if dist.has_metadata('PKG-INFO'):
                metadata = dist.get_metadata('PKG-INFO')

        if dist.has_metadata('entry_points.txt'):
            entry_points = dist.get_metadata_lines('entry_points.txt')
            package['entry_points'] = entry_points

        if dist.has_metadata('INSTALLER'):
            for line in dist.get_metadata_lines('INSTALLER'):
                if line.strip():
                    package['installer'] = line.strip()
                    break

        # @todo: Should pkg_resources.Distribution have a
        # `get_pkg_info` method?
        feed_parser = FeedParser()
        feed_parser.feed(metadata)
        pkg_info_dict = feed_parser.close()
        for key in ('metadata-version', 'summary', 'home-page', 'author',
                    'author-email', 'license'):
            package[key] = pkg_info_dict.get(key)

        # It looks like FeedParser cannot deal with repeated headers
        classifiers = []
        for line in metadata.splitlines():
            if line.startswith('Classifier: '):
                classifiers.append(line[len('Classifier: '):])
        package['classifiers'] = classifiers

        if file_list:
            package['files'] = sorted(file_list)
        yield package