示例#1
0
 def installed_location(self) -> Optional[str]:
     egg_link = egg_link_path_from_location(self.raw_name)
     if egg_link:
         location = egg_link
     elif self.location:
         location = self.location
     else:
         return None
     return normalize_path(location)
示例#2
0
def dist_location(dist: Distribution) -> str:
    """
    Get the site-packages location of this distribution. Generally
    this is dist.location, except in the case of develop-installed
    packages, where dist.location is the source code location, and we
    want to know where the egg-link file is.

    The returned location is normalized (in particular, with symlinks removed).
    """
    egg_link = egg_link_path_from_location(dist.project_name)
    if egg_link:
        return normalize_path(egg_link)
    return normalize_path(dist.location)
示例#3
0
文件: base.py 项目: DiddiLeija/pip
    def installed_location(self) -> Optional[str]:
        """The distribution's "installed" location.

        This should generally be a ``site-packages`` directory. This is
        usually ``dist.location``, except for legacy develop-installed packages,
        where ``dist.location`` is the source code location, and this is where
        the ``.egg-link`` file is.

        The returned location is normalized (in particular, with symlinks removed).
        """
        egg_link = egg_link_path_from_location(self.raw_name)
        if egg_link:
            location = egg_link
        elif self.location:
            location = self.location
        else:
            return None
        return normalize_path(location)
示例#4
0
    def from_dist(cls, dist: BaseDistribution) -> "UninstallPathSet":
        dist_location = dist.location
        info_location = dist.info_location
        if dist_location is None:
            logger.info(
                "Not uninstalling %s since it is not installed",
                dist.canonical_name,
            )
            return cls(dist)

        normalized_dist_location = normalize_path(dist_location)
        if not dist.local:
            logger.info(
                "Not uninstalling %s at %s, outside environment %s",
                dist.canonical_name,
                normalized_dist_location,
                sys.prefix,
            )
            return cls(dist)

        if normalized_dist_location in {
            p
            for p in {sysconfig.get_path("stdlib"), sysconfig.get_path("platstdlib")}
            if p
        }:
            logger.info(
                "Not uninstalling %s at %s, as it is in the standard library.",
                dist.canonical_name,
                normalized_dist_location,
            )
            return cls(dist)

        paths_to_remove = cls(dist)
        develop_egg_link = egg_link_path_from_location(dist.raw_name)

        # Distribution is installed with metadata in a "flat" .egg-info
        # directory. This means it is not a modern .dist-info installation, an
        # egg, or legacy editable.
        setuptools_flat_installation = (
            dist.installed_with_setuptools_egg_info
            and info_location is not None
            and os.path.exists(info_location)
            # If dist is editable and the location points to a ``.egg-info``,
            # we are in fact in the legacy editable case.
            and not info_location.endswith(f"{dist.setuptools_filename}.egg-info")
        )

        # Uninstall cases order do matter as in the case of 2 installs of the
        # same package, pip needs to uninstall the currently detected version
        if setuptools_flat_installation:
            if info_location is not None:
                paths_to_remove.add(info_location)
            installed_files = dist.iter_declared_entries()
            if installed_files is not None:
                for installed_file in installed_files:
                    paths_to_remove.add(os.path.join(dist_location, installed_file))
            # FIXME: need a test for this elif block
            # occurs with --single-version-externally-managed/--record outside
            # of pip
            elif dist.is_file("top_level.txt"):
                try:
                    namespace_packages = dist.read_text("namespace_packages.txt")
                except FileNotFoundError:
                    namespaces = []
                else:
                    namespaces = namespace_packages.splitlines(keepends=False)
                for top_level_pkg in [
                    p
                    for p in dist.read_text("top_level.txt").splitlines()
                    if p and p not in namespaces
                ]:
                    path = os.path.join(dist_location, top_level_pkg)
                    paths_to_remove.add(path)
                    paths_to_remove.add(f"{path}.py")
                    paths_to_remove.add(f"{path}.pyc")
                    paths_to_remove.add(f"{path}.pyo")

        elif dist.installed_by_distutils:
            raise UninstallationError(
                "Cannot uninstall {!r}. It is a distutils installed project "
                "and thus we cannot accurately determine which files belong "
                "to it which would lead to only a partial uninstall.".format(
                    dist.raw_name,
                )
            )

        elif dist.installed_as_egg:
            # package installed by easy_install
            # We cannot match on dist.egg_name because it can slightly vary
            # i.e. setuptools-0.6c11-py2.6.egg vs setuptools-0.6rc11-py2.6.egg
            paths_to_remove.add(dist_location)
            easy_install_egg = os.path.split(dist_location)[1]
            easy_install_pth = os.path.join(
                os.path.dirname(dist_location),
                "easy-install.pth",
            )
            paths_to_remove.add_pth(easy_install_pth, "./" + easy_install_egg)

        elif dist.installed_with_dist_info:
            for path in uninstallation_paths(dist):
                paths_to_remove.add(path)

        elif develop_egg_link:
            # PEP 660 modern editable is handled in the ``.dist-info`` case
            # above, so this only covers the setuptools-style editable.
            with open(develop_egg_link) as fh:
                link_pointer = os.path.normcase(fh.readline().strip())
            assert os.path.samefile(link_pointer, dist_location), (
                f"Egg-link {link_pointer} does not match installed location of "
                f"{dist.raw_name} (at {dist_location})"
            )
            paths_to_remove.add(develop_egg_link)
            easy_install_pth = os.path.join(
                os.path.dirname(develop_egg_link), "easy-install.pth"
            )
            paths_to_remove.add_pth(easy_install_pth, dist_location)

        else:
            logger.debug(
                "Not sure how to uninstall: %s - Check: %s",
                dist,
                dist_location,
            )

        if dist.in_usersite:
            bin_dir = get_bin_user()
        else:
            bin_dir = get_bin_prefix()

        # find distutils scripts= scripts
        try:
            for script in dist.iterdir("scripts"):
                paths_to_remove.add(os.path.join(bin_dir, script.name))
                if WINDOWS:
                    paths_to_remove.add(os.path.join(bin_dir, f"{script.name}.bat"))
        except (FileNotFoundError, NotADirectoryError):
            pass

        # find console_scripts and gui_scripts
        def iter_scripts_to_remove(
            dist: BaseDistribution,
            bin_dir: str,
        ) -> Iterator[str]:
            for entry_point in dist.iter_entry_points():
                if entry_point.group == "console_scripts":
                    yield from _script_names(bin_dir, entry_point.name, False)
                elif entry_point.group == "gui_scripts":
                    yield from _script_names(bin_dir, entry_point.name, True)

        for s in iter_scripts_to_remove(dist, bin_dir):
            paths_to_remove.add(s)

        return paths_to_remove
    def from_dist(cls, dist: Distribution) -> "UninstallPathSet":
        dist_path = normalize_path(dist.location)
        if not dist_is_local(dist):
            logger.info(
                "Not uninstalling %s at %s, outside environment %s",
                dist.key,
                dist_path,
                sys.prefix,
            )
            return cls(dist)

        if dist_path in {
                p
                for p in
            {sysconfig.get_path("stdlib"),
             sysconfig.get_path("platstdlib")} if p
        }:
            logger.info(
                "Not uninstalling %s at %s, as it is in the standard library.",
                dist.key,
                dist_path,
            )
            return cls(dist)

        paths_to_remove = cls(dist)
        develop_egg_link = egg_link_path_from_location(dist.project_name)
        develop_egg_link_egg_info = "{}.egg-info".format(
            pkg_resources.to_filename(dist.project_name))
        egg_info_exists = dist.egg_info and os.path.exists(dist.egg_info)
        # Special case for distutils installed package
        distutils_egg_info = getattr(dist._provider, "path", None)

        # Uninstall cases order do matter as in the case of 2 installs of the
        # same package, pip needs to uninstall the currently detected version
        if (egg_info_exists and dist.egg_info.endswith(".egg-info")
                and not dist.egg_info.endswith(develop_egg_link_egg_info)):
            # if dist.egg_info.endswith(develop_egg_link_egg_info), we
            # are in fact in the develop_egg_link case
            paths_to_remove.add(dist.egg_info)
            if dist.has_metadata("installed-files.txt"):
                for installed_file in dist.get_metadata(
                        "installed-files.txt").splitlines():
                    path = os.path.normpath(
                        os.path.join(dist.egg_info, installed_file))
                    paths_to_remove.add(path)
            # FIXME: need a test for this elif block
            # occurs with --single-version-externally-managed/--record outside
            # of pip
            elif dist.has_metadata("top_level.txt"):
                if dist.has_metadata("namespace_packages.txt"):
                    namespaces = dist.get_metadata("namespace_packages.txt")
                else:
                    namespaces = []
                for top_level_pkg in [
                        p for p in dist.get_metadata(
                            "top_level.txt").splitlines()
                        if p and p not in namespaces
                ]:
                    path = os.path.join(dist.location, top_level_pkg)
                    paths_to_remove.add(path)
                    paths_to_remove.add(path + ".py")
                    paths_to_remove.add(path + ".pyc")
                    paths_to_remove.add(path + ".pyo")

        elif distutils_egg_info:
            raise UninstallationError(
                "Cannot uninstall {!r}. It is a distutils installed project "
                "and thus we cannot accurately determine which files belong "
                "to it which would lead to only a partial uninstall.".format(
                    dist.project_name, ))

        elif dist.location.endswith(".egg"):
            # package installed by easy_install
            # We cannot match on dist.egg_name because it can slightly vary
            # i.e. setuptools-0.6c11-py2.6.egg vs setuptools-0.6rc11-py2.6.egg
            paths_to_remove.add(dist.location)
            easy_install_egg = os.path.split(dist.location)[1]
            easy_install_pth = os.path.join(os.path.dirname(dist.location),
                                            "easy-install.pth")
            paths_to_remove.add_pth(easy_install_pth, "./" + easy_install_egg)

        elif egg_info_exists and dist.egg_info.endswith(".dist-info"):
            for path in uninstallation_paths(dist):
                paths_to_remove.add(path)

        elif develop_egg_link:
            # develop egg
            with open(develop_egg_link) as fh:
                link_pointer = os.path.normcase(fh.readline().strip())
            assert (
                link_pointer == dist.location
            ), "Egg-link {} does not match installed location of {} (at {})".format(
                link_pointer, dist.project_name, dist.location)
            paths_to_remove.add(develop_egg_link)
            easy_install_pth = os.path.join(os.path.dirname(develop_egg_link),
                                            "easy-install.pth")
            paths_to_remove.add_pth(easy_install_pth, dist.location)

        else:
            logger.debug(
                "Not sure how to uninstall: %s - Check: %s",
                dist,
                dist.location,
            )

        # find distutils scripts= scripts
        if dist.has_metadata("scripts") and dist.metadata_isdir("scripts"):
            for script in dist.metadata_listdir("scripts"):
                if dist_in_usersite(dist):
                    bin_dir = get_bin_user()
                else:
                    bin_dir = get_bin_prefix()
                paths_to_remove.add(os.path.join(bin_dir, script))
                if WINDOWS:
                    paths_to_remove.add(os.path.join(bin_dir, script) + ".bat")

        # find console_scripts
        _scripts_to_remove = []
        console_scripts = dist.get_entry_map(group="console_scripts")
        for name in console_scripts.keys():
            _scripts_to_remove.extend(_script_names(dist, name, False))
        # find gui_scripts
        gui_scripts = dist.get_entry_map(group="gui_scripts")
        for name in gui_scripts.keys():
            _scripts_to_remove.extend(_script_names(dist, name, True))

        for s in _scripts_to_remove:
            paths_to_remove.add(s)

        return paths_to_remove
示例#6
0
 def test_noegglink_in_sitepkgs_venv_global(self) -> None:
     self.mock_virtualenv_no_global.return_value = False
     self.mock_running_under_virtualenv.return_value = True
     self.mock_isfile.return_value = False
     assert egg_link_path_from_location(self.mock_dist.project_name) is None
示例#7
0
 def test_egglink_in_both_venv_global(self) -> None:
     self.mock_virtualenv_no_global.return_value = False
     self.mock_running_under_virtualenv.return_value = True
     self.mock_isfile.return_value = True
     assert (egg_link_path_from_location(
         self.mock_dist.project_name) == self.site_packages_egglink)
示例#8
0
 def test_egglink_in_sitepkgs_venv_noglobal(self) -> None:
     self.mock_virtualenv_no_global.return_value = True
     self.mock_running_under_virtualenv.return_value = True
     self.mock_isfile.side_effect = self.eggLinkInSitePackages
     assert (egg_link_path_from_location(
         self.mock_dist.project_name) == self.site_packages_egglink)
示例#9
0
 def test_egglink_in_usersite_venv_global(self) -> None:
     self.mock_virtualenv_no_global.return_value = False
     self.mock_running_under_virtualenv.return_value = True
     self.mock_isfile.side_effect = self.eggLinkInUserSite
     assert (egg_link_path_from_location(
         self.mock_dist.project_name) == self.user_site_egglink)