You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			775 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			Python
		
	
			
		
		
	
	
			775 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			Python
		
	
import errno
 | 
						|
import json
 | 
						|
import operator
 | 
						|
import os
 | 
						|
import shutil
 | 
						|
import site
 | 
						|
from optparse import SUPPRESS_HELP, Values
 | 
						|
from typing import List, Optional
 | 
						|
 | 
						|
from pip._vendor.rich import print_json
 | 
						|
 | 
						|
from pip._internal.cache import WheelCache
 | 
						|
from pip._internal.cli import cmdoptions
 | 
						|
from pip._internal.cli.cmdoptions import make_target_python
 | 
						|
from pip._internal.cli.req_command import (
 | 
						|
    RequirementCommand,
 | 
						|
    warn_if_run_as_root,
 | 
						|
    with_cleanup,
 | 
						|
)
 | 
						|
from pip._internal.cli.status_codes import ERROR, SUCCESS
 | 
						|
from pip._internal.exceptions import CommandError, InstallationError
 | 
						|
from pip._internal.locations import get_scheme
 | 
						|
from pip._internal.metadata import get_environment
 | 
						|
from pip._internal.models.installation_report import InstallationReport
 | 
						|
from pip._internal.operations.build.build_tracker import get_build_tracker
 | 
						|
from pip._internal.operations.check import ConflictDetails, check_install_conflicts
 | 
						|
from pip._internal.req import install_given_reqs
 | 
						|
from pip._internal.req.req_install import (
 | 
						|
    InstallRequirement,
 | 
						|
    check_legacy_setup_py_options,
 | 
						|
)
 | 
						|
from pip._internal.utils.compat import WINDOWS
 | 
						|
from pip._internal.utils.filesystem import test_writable_dir
 | 
						|
from pip._internal.utils.logging import getLogger
 | 
						|
from pip._internal.utils.misc import (
 | 
						|
    check_externally_managed,
 | 
						|
    ensure_dir,
 | 
						|
    get_pip_version,
 | 
						|
    protect_pip_from_modification_on_windows,
 | 
						|
    write_output,
 | 
						|
)
 | 
						|
from pip._internal.utils.temp_dir import TempDirectory
 | 
						|
from pip._internal.utils.virtualenv import (
 | 
						|
    running_under_virtualenv,
 | 
						|
    virtualenv_no_global,
 | 
						|
)
 | 
						|
from pip._internal.wheel_builder import build, should_build_for_install_command
 | 
						|
 | 
						|
logger = getLogger(__name__)
 | 
						|
 | 
						|
 | 
						|
class InstallCommand(RequirementCommand):
 | 
						|
    """
 | 
						|
    Install packages from:
 | 
						|
 | 
						|
    - PyPI (and other indexes) using requirement specifiers.
 | 
						|
    - VCS project urls.
 | 
						|
    - Local project directories.
 | 
						|
    - Local or remote source archives.
 | 
						|
 | 
						|
    pip also supports installing from "requirements files", which provide
 | 
						|
    an easy way to specify a whole environment to be installed.
 | 
						|
    """
 | 
						|
 | 
						|
    usage = """
 | 
						|
      %prog [options] <requirement specifier> [package-index-options] ...
 | 
						|
      %prog [options] -r <requirements file> [package-index-options] ...
 | 
						|
      %prog [options] [-e] <vcs project url> ...
 | 
						|
      %prog [options] [-e] <local project path> ...
 | 
						|
      %prog [options] <archive url/path> ..."""
 | 
						|
 | 
						|
    def add_options(self) -> None:
 | 
						|
        self.cmd_opts.add_option(cmdoptions.requirements())
 | 
						|
        self.cmd_opts.add_option(cmdoptions.constraints())
 | 
						|
        self.cmd_opts.add_option(cmdoptions.no_deps())
 | 
						|
        self.cmd_opts.add_option(cmdoptions.pre())
 | 
						|
 | 
						|
        self.cmd_opts.add_option(cmdoptions.editable())
 | 
						|
        self.cmd_opts.add_option(
 | 
						|
            "--dry-run",
 | 
						|
            action="store_true",
 | 
						|
            dest="dry_run",
 | 
						|
            default=False,
 | 
						|
            help=(
 | 
						|
                "Don't actually install anything, just print what would be. "
 | 
						|
                "Can be used in combination with --ignore-installed "
 | 
						|
                "to 'resolve' the requirements."
 | 
						|
            ),
 | 
						|
        )
 | 
						|
        self.cmd_opts.add_option(
 | 
						|
            "-t",
 | 
						|
            "--target",
 | 
						|
            dest="target_dir",
 | 
						|
            metavar="dir",
 | 
						|
            default=None,
 | 
						|
            help=(
 | 
						|
                "Install packages into <dir>. "
 | 
						|
                "By default this will not replace existing files/folders in "
 | 
						|
                "<dir>. Use --upgrade to replace existing packages in <dir> "
 | 
						|
                "with new versions."
 | 
						|
            ),
 | 
						|
        )
 | 
						|
        cmdoptions.add_target_python_options(self.cmd_opts)
 | 
						|
 | 
						|
        self.cmd_opts.add_option(
 | 
						|
            "--user",
 | 
						|
            dest="use_user_site",
 | 
						|
            action="store_true",
 | 
						|
            help=(
 | 
						|
                "Install to the Python user install directory for your "
 | 
						|
                "platform. Typically ~/.local/, or %APPDATA%\\Python on "
 | 
						|
                "Windows. (See the Python documentation for site.USER_BASE "
 | 
						|
                "for full details.)"
 | 
						|
            ),
 | 
						|
        )
 | 
						|
        self.cmd_opts.add_option(
 | 
						|
            "--no-user",
 | 
						|
            dest="use_user_site",
 | 
						|
            action="store_false",
 | 
						|
            help=SUPPRESS_HELP,
 | 
						|
        )
 | 
						|
        self.cmd_opts.add_option(
 | 
						|
            "--root",
 | 
						|
            dest="root_path",
 | 
						|
            metavar="dir",
 | 
						|
            default=None,
 | 
						|
            help="Install everything relative to this alternate root directory.",
 | 
						|
        )
 | 
						|
        self.cmd_opts.add_option(
 | 
						|
            "--prefix",
 | 
						|
            dest="prefix_path",
 | 
						|
            metavar="dir",
 | 
						|
            default=None,
 | 
						|
            help=(
 | 
						|
                "Installation prefix where lib, bin and other top-level "
 | 
						|
                "folders are placed. Note that the resulting installation may "
 | 
						|
                "contain scripts and other resources which reference the "
 | 
						|
                "Python interpreter of pip, and not that of ``--prefix``. "
 | 
						|
                "See also the ``--python`` option if the intention is to "
 | 
						|
                "install packages into another (possibly pip-free) "
 | 
						|
                "environment."
 | 
						|
            ),
 | 
						|
        )
 | 
						|
 | 
						|
        self.cmd_opts.add_option(cmdoptions.src())
 | 
						|
 | 
						|
        self.cmd_opts.add_option(
 | 
						|
            "-U",
 | 
						|
            "--upgrade",
 | 
						|
            dest="upgrade",
 | 
						|
            action="store_true",
 | 
						|
            help=(
 | 
						|
                "Upgrade all specified packages to the newest available "
 | 
						|
                "version. The handling of dependencies depends on the "
 | 
						|
                "upgrade-strategy used."
 | 
						|
            ),
 | 
						|
        )
 | 
						|
 | 
						|
        self.cmd_opts.add_option(
 | 
						|
            "--upgrade-strategy",
 | 
						|
            dest="upgrade_strategy",
 | 
						|
            default="only-if-needed",
 | 
						|
            choices=["only-if-needed", "eager"],
 | 
						|
            help=(
 | 
						|
                "Determines how dependency upgrading should be handled "
 | 
						|
                "[default: %default]. "
 | 
						|
                '"eager" - dependencies are upgraded regardless of '
 | 
						|
                "whether the currently installed version satisfies the "
 | 
						|
                "requirements of the upgraded package(s). "
 | 
						|
                '"only-if-needed" -  are upgraded only when they do not '
 | 
						|
                "satisfy the requirements of the upgraded package(s)."
 | 
						|
            ),
 | 
						|
        )
 | 
						|
 | 
						|
        self.cmd_opts.add_option(
 | 
						|
            "--force-reinstall",
 | 
						|
            dest="force_reinstall",
 | 
						|
            action="store_true",
 | 
						|
            help="Reinstall all packages even if they are already up-to-date.",
 | 
						|
        )
 | 
						|
 | 
						|
        self.cmd_opts.add_option(
 | 
						|
            "-I",
 | 
						|
            "--ignore-installed",
 | 
						|
            dest="ignore_installed",
 | 
						|
            action="store_true",
 | 
						|
            help=(
 | 
						|
                "Ignore the installed packages, overwriting them. "
 | 
						|
                "This can break your system if the existing package "
 | 
						|
                "is of a different version or was installed "
 | 
						|
                "with a different package manager!"
 | 
						|
            ),
 | 
						|
        )
 | 
						|
 | 
						|
        self.cmd_opts.add_option(cmdoptions.ignore_requires_python())
 | 
						|
        self.cmd_opts.add_option(cmdoptions.no_build_isolation())
 | 
						|
        self.cmd_opts.add_option(cmdoptions.use_pep517())
 | 
						|
        self.cmd_opts.add_option(cmdoptions.no_use_pep517())
 | 
						|
        self.cmd_opts.add_option(cmdoptions.check_build_deps())
 | 
						|
        self.cmd_opts.add_option(cmdoptions.override_externally_managed())
 | 
						|
 | 
						|
        self.cmd_opts.add_option(cmdoptions.config_settings())
 | 
						|
        self.cmd_opts.add_option(cmdoptions.global_options())
 | 
						|
 | 
						|
        self.cmd_opts.add_option(
 | 
						|
            "--compile",
 | 
						|
            action="store_true",
 | 
						|
            dest="compile",
 | 
						|
            default=True,
 | 
						|
            help="Compile Python source files to bytecode",
 | 
						|
        )
 | 
						|
 | 
						|
        self.cmd_opts.add_option(
 | 
						|
            "--no-compile",
 | 
						|
            action="store_false",
 | 
						|
            dest="compile",
 | 
						|
            help="Do not compile Python source files to bytecode",
 | 
						|
        )
 | 
						|
 | 
						|
        self.cmd_opts.add_option(
 | 
						|
            "--no-warn-script-location",
 | 
						|
            action="store_false",
 | 
						|
            dest="warn_script_location",
 | 
						|
            default=True,
 | 
						|
            help="Do not warn when installing scripts outside PATH",
 | 
						|
        )
 | 
						|
        self.cmd_opts.add_option(
 | 
						|
            "--no-warn-conflicts",
 | 
						|
            action="store_false",
 | 
						|
            dest="warn_about_conflicts",
 | 
						|
            default=True,
 | 
						|
            help="Do not warn about broken dependencies",
 | 
						|
        )
 | 
						|
        self.cmd_opts.add_option(cmdoptions.no_binary())
 | 
						|
        self.cmd_opts.add_option(cmdoptions.only_binary())
 | 
						|
        self.cmd_opts.add_option(cmdoptions.prefer_binary())
 | 
						|
        self.cmd_opts.add_option(cmdoptions.require_hashes())
 | 
						|
        self.cmd_opts.add_option(cmdoptions.progress_bar())
 | 
						|
        self.cmd_opts.add_option(cmdoptions.root_user_action())
 | 
						|
 | 
						|
        index_opts = cmdoptions.make_option_group(
 | 
						|
            cmdoptions.index_group,
 | 
						|
            self.parser,
 | 
						|
        )
 | 
						|
 | 
						|
        self.parser.insert_option_group(0, index_opts)
 | 
						|
        self.parser.insert_option_group(0, self.cmd_opts)
 | 
						|
 | 
						|
        self.cmd_opts.add_option(
 | 
						|
            "--report",
 | 
						|
            dest="json_report_file",
 | 
						|
            metavar="file",
 | 
						|
            default=None,
 | 
						|
            help=(
 | 
						|
                "Generate a JSON file describing what pip did to install "
 | 
						|
                "the provided requirements. "
 | 
						|
                "Can be used in combination with --dry-run and --ignore-installed "
 | 
						|
                "to 'resolve' the requirements. "
 | 
						|
                "When - is used as file name it writes to stdout. "
 | 
						|
                "When writing to stdout, please combine with the --quiet option "
 | 
						|
                "to avoid mixing pip logging output with JSON output."
 | 
						|
            ),
 | 
						|
        )
 | 
						|
 | 
						|
    @with_cleanup
 | 
						|
    def run(self, options: Values, args: List[str]) -> int:
 | 
						|
        if options.use_user_site and options.target_dir is not None:
 | 
						|
            raise CommandError("Can not combine '--user' and '--target'")
 | 
						|
 | 
						|
        # Check whether the environment we're installing into is externally
 | 
						|
        # managed, as specified in PEP 668. Specifying --root, --target, or
 | 
						|
        # --prefix disables the check, since there's no reliable way to locate
 | 
						|
        # the EXTERNALLY-MANAGED file for those cases. An exception is also
 | 
						|
        # made specifically for "--dry-run --report" for convenience.
 | 
						|
        installing_into_current_environment = (
 | 
						|
            not (options.dry_run and options.json_report_file)
 | 
						|
            and options.root_path is None
 | 
						|
            and options.target_dir is None
 | 
						|
            and options.prefix_path is None
 | 
						|
        )
 | 
						|
        if (
 | 
						|
            installing_into_current_environment
 | 
						|
            and not options.override_externally_managed
 | 
						|
        ):
 | 
						|
            check_externally_managed()
 | 
						|
 | 
						|
        upgrade_strategy = "to-satisfy-only"
 | 
						|
        if options.upgrade:
 | 
						|
            upgrade_strategy = options.upgrade_strategy
 | 
						|
 | 
						|
        cmdoptions.check_dist_restriction(options, check_target=True)
 | 
						|
 | 
						|
        logger.verbose("Using %s", get_pip_version())
 | 
						|
        options.use_user_site = decide_user_install(
 | 
						|
            options.use_user_site,
 | 
						|
            prefix_path=options.prefix_path,
 | 
						|
            target_dir=options.target_dir,
 | 
						|
            root_path=options.root_path,
 | 
						|
            isolated_mode=options.isolated_mode,
 | 
						|
        )
 | 
						|
 | 
						|
        target_temp_dir: Optional[TempDirectory] = None
 | 
						|
        target_temp_dir_path: Optional[str] = None
 | 
						|
        if options.target_dir:
 | 
						|
            options.ignore_installed = True
 | 
						|
            options.target_dir = os.path.abspath(options.target_dir)
 | 
						|
            if (
 | 
						|
                # fmt: off
 | 
						|
                os.path.exists(options.target_dir) and
 | 
						|
                not os.path.isdir(options.target_dir)
 | 
						|
                # fmt: on
 | 
						|
            ):
 | 
						|
                raise CommandError(
 | 
						|
                    "Target path exists but is not a directory, will not continue."
 | 
						|
                )
 | 
						|
 | 
						|
            # Create a target directory for using with the target option
 | 
						|
            target_temp_dir = TempDirectory(kind="target")
 | 
						|
            target_temp_dir_path = target_temp_dir.path
 | 
						|
            self.enter_context(target_temp_dir)
 | 
						|
 | 
						|
        global_options = options.global_options or []
 | 
						|
 | 
						|
        session = self.get_default_session(options)
 | 
						|
 | 
						|
        target_python = make_target_python(options)
 | 
						|
        finder = self._build_package_finder(
 | 
						|
            options=options,
 | 
						|
            session=session,
 | 
						|
            target_python=target_python,
 | 
						|
            ignore_requires_python=options.ignore_requires_python,
 | 
						|
        )
 | 
						|
        build_tracker = self.enter_context(get_build_tracker())
 | 
						|
 | 
						|
        directory = TempDirectory(
 | 
						|
            delete=not options.no_clean,
 | 
						|
            kind="install",
 | 
						|
            globally_managed=True,
 | 
						|
        )
 | 
						|
 | 
						|
        try:
 | 
						|
            reqs = self.get_requirements(args, options, finder, session)
 | 
						|
            check_legacy_setup_py_options(options, reqs)
 | 
						|
 | 
						|
            wheel_cache = WheelCache(options.cache_dir)
 | 
						|
 | 
						|
            # Only when installing is it permitted to use PEP 660.
 | 
						|
            # In other circumstances (pip wheel, pip download) we generate
 | 
						|
            # regular (i.e. non editable) metadata and wheels.
 | 
						|
            for req in reqs:
 | 
						|
                req.permit_editable_wheels = True
 | 
						|
 | 
						|
            preparer = self.make_requirement_preparer(
 | 
						|
                temp_build_dir=directory,
 | 
						|
                options=options,
 | 
						|
                build_tracker=build_tracker,
 | 
						|
                session=session,
 | 
						|
                finder=finder,
 | 
						|
                use_user_site=options.use_user_site,
 | 
						|
                verbosity=self.verbosity,
 | 
						|
            )
 | 
						|
            resolver = self.make_resolver(
 | 
						|
                preparer=preparer,
 | 
						|
                finder=finder,
 | 
						|
                options=options,
 | 
						|
                wheel_cache=wheel_cache,
 | 
						|
                use_user_site=options.use_user_site,
 | 
						|
                ignore_installed=options.ignore_installed,
 | 
						|
                ignore_requires_python=options.ignore_requires_python,
 | 
						|
                force_reinstall=options.force_reinstall,
 | 
						|
                upgrade_strategy=upgrade_strategy,
 | 
						|
                use_pep517=options.use_pep517,
 | 
						|
            )
 | 
						|
 | 
						|
            self.trace_basic_info(finder)
 | 
						|
 | 
						|
            requirement_set = resolver.resolve(
 | 
						|
                reqs, check_supported_wheels=not options.target_dir
 | 
						|
            )
 | 
						|
 | 
						|
            if options.json_report_file:
 | 
						|
                report = InstallationReport(requirement_set.requirements_to_install)
 | 
						|
                if options.json_report_file == "-":
 | 
						|
                    print_json(data=report.to_dict())
 | 
						|
                else:
 | 
						|
                    with open(options.json_report_file, "w", encoding="utf-8") as f:
 | 
						|
                        json.dump(report.to_dict(), f, indent=2, ensure_ascii=False)
 | 
						|
 | 
						|
            if options.dry_run:
 | 
						|
                # In non dry-run mode, the legacy versions and specifiers check
 | 
						|
                # will be done as part of conflict detection.
 | 
						|
                requirement_set.warn_legacy_versions_and_specifiers()
 | 
						|
                would_install_items = sorted(
 | 
						|
                    (r.metadata["name"], r.metadata["version"])
 | 
						|
                    for r in requirement_set.requirements_to_install
 | 
						|
                )
 | 
						|
                if would_install_items:
 | 
						|
                    write_output(
 | 
						|
                        "Would install %s",
 | 
						|
                        " ".join("-".join(item) for item in would_install_items),
 | 
						|
                    )
 | 
						|
                return SUCCESS
 | 
						|
 | 
						|
            try:
 | 
						|
                pip_req = requirement_set.get_requirement("pip")
 | 
						|
            except KeyError:
 | 
						|
                modifying_pip = False
 | 
						|
            else:
 | 
						|
                # If we're not replacing an already installed pip,
 | 
						|
                # we're not modifying it.
 | 
						|
                modifying_pip = pip_req.satisfied_by is None
 | 
						|
            protect_pip_from_modification_on_windows(modifying_pip=modifying_pip)
 | 
						|
 | 
						|
            reqs_to_build = [
 | 
						|
                r
 | 
						|
                for r in requirement_set.requirements.values()
 | 
						|
                if should_build_for_install_command(r)
 | 
						|
            ]
 | 
						|
 | 
						|
            _, build_failures = build(
 | 
						|
                reqs_to_build,
 | 
						|
                wheel_cache=wheel_cache,
 | 
						|
                verify=True,
 | 
						|
                build_options=[],
 | 
						|
                global_options=global_options,
 | 
						|
            )
 | 
						|
 | 
						|
            if build_failures:
 | 
						|
                raise InstallationError(
 | 
						|
                    "Could not build wheels for {}, which is required to "
 | 
						|
                    "install pyproject.toml-based projects".format(
 | 
						|
                        ", ".join(r.name for r in build_failures)  # type: ignore
 | 
						|
                    )
 | 
						|
                )
 | 
						|
 | 
						|
            to_install = resolver.get_installation_order(requirement_set)
 | 
						|
 | 
						|
            # Check for conflicts in the package set we're installing.
 | 
						|
            conflicts: Optional[ConflictDetails] = None
 | 
						|
            should_warn_about_conflicts = (
 | 
						|
                not options.ignore_dependencies and options.warn_about_conflicts
 | 
						|
            )
 | 
						|
            if should_warn_about_conflicts:
 | 
						|
                conflicts = self._determine_conflicts(to_install)
 | 
						|
 | 
						|
            # Don't warn about script install locations if
 | 
						|
            # --target or --prefix has been specified
 | 
						|
            warn_script_location = options.warn_script_location
 | 
						|
            if options.target_dir or options.prefix_path:
 | 
						|
                warn_script_location = False
 | 
						|
 | 
						|
            installed = install_given_reqs(
 | 
						|
                to_install,
 | 
						|
                global_options,
 | 
						|
                root=options.root_path,
 | 
						|
                home=target_temp_dir_path,
 | 
						|
                prefix=options.prefix_path,
 | 
						|
                warn_script_location=warn_script_location,
 | 
						|
                use_user_site=options.use_user_site,
 | 
						|
                pycompile=options.compile,
 | 
						|
            )
 | 
						|
 | 
						|
            lib_locations = get_lib_location_guesses(
 | 
						|
                user=options.use_user_site,
 | 
						|
                home=target_temp_dir_path,
 | 
						|
                root=options.root_path,
 | 
						|
                prefix=options.prefix_path,
 | 
						|
                isolated=options.isolated_mode,
 | 
						|
            )
 | 
						|
            env = get_environment(lib_locations)
 | 
						|
 | 
						|
            installed.sort(key=operator.attrgetter("name"))
 | 
						|
            items = []
 | 
						|
            for result in installed:
 | 
						|
                item = result.name
 | 
						|
                try:
 | 
						|
                    installed_dist = env.get_distribution(item)
 | 
						|
                    if installed_dist is not None:
 | 
						|
                        item = f"{item}-{installed_dist.version}"
 | 
						|
                except Exception:
 | 
						|
                    pass
 | 
						|
                items.append(item)
 | 
						|
 | 
						|
            if conflicts is not None:
 | 
						|
                self._warn_about_conflicts(
 | 
						|
                    conflicts,
 | 
						|
                    resolver_variant=self.determine_resolver_variant(options),
 | 
						|
                )
 | 
						|
 | 
						|
            installed_desc = " ".join(items)
 | 
						|
            if installed_desc:
 | 
						|
                write_output(
 | 
						|
                    "Successfully installed %s",
 | 
						|
                    installed_desc,
 | 
						|
                )
 | 
						|
        except OSError as error:
 | 
						|
            show_traceback = self.verbosity >= 1
 | 
						|
 | 
						|
            message = create_os_error_message(
 | 
						|
                error,
 | 
						|
                show_traceback,
 | 
						|
                options.use_user_site,
 | 
						|
            )
 | 
						|
            logger.error(message, exc_info=show_traceback)
 | 
						|
 | 
						|
            return ERROR
 | 
						|
 | 
						|
        if options.target_dir:
 | 
						|
            assert target_temp_dir
 | 
						|
            self._handle_target_dir(
 | 
						|
                options.target_dir, target_temp_dir, options.upgrade
 | 
						|
            )
 | 
						|
        if options.root_user_action == "warn":
 | 
						|
            warn_if_run_as_root()
 | 
						|
        return SUCCESS
 | 
						|
 | 
						|
    def _handle_target_dir(
 | 
						|
        self, target_dir: str, target_temp_dir: TempDirectory, upgrade: bool
 | 
						|
    ) -> None:
 | 
						|
        ensure_dir(target_dir)
 | 
						|
 | 
						|
        # Checking both purelib and platlib directories for installed
 | 
						|
        # packages to be moved to target directory
 | 
						|
        lib_dir_list = []
 | 
						|
 | 
						|
        # Checking both purelib and platlib directories for installed
 | 
						|
        # packages to be moved to target directory
 | 
						|
        scheme = get_scheme("", home=target_temp_dir.path)
 | 
						|
        purelib_dir = scheme.purelib
 | 
						|
        platlib_dir = scheme.platlib
 | 
						|
        data_dir = scheme.data
 | 
						|
 | 
						|
        if os.path.exists(purelib_dir):
 | 
						|
            lib_dir_list.append(purelib_dir)
 | 
						|
        if os.path.exists(platlib_dir) and platlib_dir != purelib_dir:
 | 
						|
            lib_dir_list.append(platlib_dir)
 | 
						|
        if os.path.exists(data_dir):
 | 
						|
            lib_dir_list.append(data_dir)
 | 
						|
 | 
						|
        for lib_dir in lib_dir_list:
 | 
						|
            for item in os.listdir(lib_dir):
 | 
						|
                if lib_dir == data_dir:
 | 
						|
                    ddir = os.path.join(data_dir, item)
 | 
						|
                    if any(s.startswith(ddir) for s in lib_dir_list[:-1]):
 | 
						|
                        continue
 | 
						|
                target_item_dir = os.path.join(target_dir, item)
 | 
						|
                if os.path.exists(target_item_dir):
 | 
						|
                    if not upgrade:
 | 
						|
                        logger.warning(
 | 
						|
                            "Target directory %s already exists. Specify "
 | 
						|
                            "--upgrade to force replacement.",
 | 
						|
                            target_item_dir,
 | 
						|
                        )
 | 
						|
                        continue
 | 
						|
                    if os.path.islink(target_item_dir):
 | 
						|
                        logger.warning(
 | 
						|
                            "Target directory %s already exists and is "
 | 
						|
                            "a link. pip will not automatically replace "
 | 
						|
                            "links, please remove if replacement is "
 | 
						|
                            "desired.",
 | 
						|
                            target_item_dir,
 | 
						|
                        )
 | 
						|
                        continue
 | 
						|
                    if os.path.isdir(target_item_dir):
 | 
						|
                        shutil.rmtree(target_item_dir)
 | 
						|
                    else:
 | 
						|
                        os.remove(target_item_dir)
 | 
						|
 | 
						|
                shutil.move(os.path.join(lib_dir, item), target_item_dir)
 | 
						|
 | 
						|
    def _determine_conflicts(
 | 
						|
        self, to_install: List[InstallRequirement]
 | 
						|
    ) -> Optional[ConflictDetails]:
 | 
						|
        try:
 | 
						|
            return check_install_conflicts(to_install)
 | 
						|
        except Exception:
 | 
						|
            logger.exception(
 | 
						|
                "Error while checking for conflicts. Please file an issue on "
 | 
						|
                "pip's issue tracker: https://github.com/pypa/pip/issues/new"
 | 
						|
            )
 | 
						|
            return None
 | 
						|
 | 
						|
    def _warn_about_conflicts(
 | 
						|
        self, conflict_details: ConflictDetails, resolver_variant: str
 | 
						|
    ) -> None:
 | 
						|
        package_set, (missing, conflicting) = conflict_details
 | 
						|
        if not missing and not conflicting:
 | 
						|
            return
 | 
						|
 | 
						|
        parts: List[str] = []
 | 
						|
        if resolver_variant == "legacy":
 | 
						|
            parts.append(
 | 
						|
                "pip's legacy dependency resolver does not consider dependency "
 | 
						|
                "conflicts when selecting packages. This behaviour is the "
 | 
						|
                "source of the following dependency conflicts."
 | 
						|
            )
 | 
						|
        else:
 | 
						|
            assert resolver_variant == "resolvelib"
 | 
						|
            parts.append(
 | 
						|
                "pip's dependency resolver does not currently take into account "
 | 
						|
                "all the packages that are installed. This behaviour is the "
 | 
						|
                "source of the following dependency conflicts."
 | 
						|
            )
 | 
						|
 | 
						|
        # NOTE: There is some duplication here, with commands/check.py
 | 
						|
        for project_name in missing:
 | 
						|
            version = package_set[project_name][0]
 | 
						|
            for dependency in missing[project_name]:
 | 
						|
                message = (
 | 
						|
                    f"{project_name} {version} requires {dependency[1]}, "
 | 
						|
                    "which is not installed."
 | 
						|
                )
 | 
						|
                parts.append(message)
 | 
						|
 | 
						|
        for project_name in conflicting:
 | 
						|
            version = package_set[project_name][0]
 | 
						|
            for dep_name, dep_version, req in conflicting[project_name]:
 | 
						|
                message = (
 | 
						|
                    "{name} {version} requires {requirement}, but {you} have "
 | 
						|
                    "{dep_name} {dep_version} which is incompatible."
 | 
						|
                ).format(
 | 
						|
                    name=project_name,
 | 
						|
                    version=version,
 | 
						|
                    requirement=req,
 | 
						|
                    dep_name=dep_name,
 | 
						|
                    dep_version=dep_version,
 | 
						|
                    you=("you" if resolver_variant == "resolvelib" else "you'll"),
 | 
						|
                )
 | 
						|
                parts.append(message)
 | 
						|
 | 
						|
        logger.critical("\n".join(parts))
 | 
						|
 | 
						|
 | 
						|
def get_lib_location_guesses(
 | 
						|
    user: bool = False,
 | 
						|
    home: Optional[str] = None,
 | 
						|
    root: Optional[str] = None,
 | 
						|
    isolated: bool = False,
 | 
						|
    prefix: Optional[str] = None,
 | 
						|
) -> List[str]:
 | 
						|
    scheme = get_scheme(
 | 
						|
        "",
 | 
						|
        user=user,
 | 
						|
        home=home,
 | 
						|
        root=root,
 | 
						|
        isolated=isolated,
 | 
						|
        prefix=prefix,
 | 
						|
    )
 | 
						|
    return [scheme.purelib, scheme.platlib]
 | 
						|
 | 
						|
 | 
						|
def site_packages_writable(root: Optional[str], isolated: bool) -> bool:
 | 
						|
    return all(
 | 
						|
        test_writable_dir(d)
 | 
						|
        for d in set(get_lib_location_guesses(root=root, isolated=isolated))
 | 
						|
    )
 | 
						|
 | 
						|
 | 
						|
def decide_user_install(
 | 
						|
    use_user_site: Optional[bool],
 | 
						|
    prefix_path: Optional[str] = None,
 | 
						|
    target_dir: Optional[str] = None,
 | 
						|
    root_path: Optional[str] = None,
 | 
						|
    isolated_mode: bool = False,
 | 
						|
) -> bool:
 | 
						|
    """Determine whether to do a user install based on the input options.
 | 
						|
 | 
						|
    If use_user_site is False, no additional checks are done.
 | 
						|
    If use_user_site is True, it is checked for compatibility with other
 | 
						|
    options.
 | 
						|
    If use_user_site is None, the default behaviour depends on the environment,
 | 
						|
    which is provided by the other arguments.
 | 
						|
    """
 | 
						|
    # In some cases (config from tox), use_user_site can be set to an integer
 | 
						|
    # rather than a bool, which 'use_user_site is False' wouldn't catch.
 | 
						|
    if (use_user_site is not None) and (not use_user_site):
 | 
						|
        logger.debug("Non-user install by explicit request")
 | 
						|
        return False
 | 
						|
 | 
						|
    if use_user_site:
 | 
						|
        if prefix_path:
 | 
						|
            raise CommandError(
 | 
						|
                "Can not combine '--user' and '--prefix' as they imply "
 | 
						|
                "different installation locations"
 | 
						|
            )
 | 
						|
        if virtualenv_no_global():
 | 
						|
            raise InstallationError(
 | 
						|
                "Can not perform a '--user' install. User site-packages "
 | 
						|
                "are not visible in this virtualenv."
 | 
						|
            )
 | 
						|
        logger.debug("User install by explicit request")
 | 
						|
        return True
 | 
						|
 | 
						|
    # If we are here, user installs have not been explicitly requested/avoided
 | 
						|
    assert use_user_site is None
 | 
						|
 | 
						|
    # user install incompatible with --prefix/--target
 | 
						|
    if prefix_path or target_dir:
 | 
						|
        logger.debug("Non-user install due to --prefix or --target option")
 | 
						|
        return False
 | 
						|
 | 
						|
    # If user installs are not enabled, choose a non-user install
 | 
						|
    if not site.ENABLE_USER_SITE:
 | 
						|
        logger.debug("Non-user install because user site-packages disabled")
 | 
						|
        return False
 | 
						|
 | 
						|
    # If we have permission for a non-user install, do that,
 | 
						|
    # otherwise do a user install.
 | 
						|
    if site_packages_writable(root=root_path, isolated=isolated_mode):
 | 
						|
        logger.debug("Non-user install because site-packages writeable")
 | 
						|
        return False
 | 
						|
 | 
						|
    logger.info(
 | 
						|
        "Defaulting to user installation because normal site-packages "
 | 
						|
        "is not writeable"
 | 
						|
    )
 | 
						|
    return True
 | 
						|
 | 
						|
 | 
						|
def create_os_error_message(
 | 
						|
    error: OSError, show_traceback: bool, using_user_site: bool
 | 
						|
) -> str:
 | 
						|
    """Format an error message for an OSError
 | 
						|
 | 
						|
    It may occur anytime during the execution of the install command.
 | 
						|
    """
 | 
						|
    parts = []
 | 
						|
 | 
						|
    # Mention the error if we are not going to show a traceback
 | 
						|
    parts.append("Could not install packages due to an OSError")
 | 
						|
    if not show_traceback:
 | 
						|
        parts.append(": ")
 | 
						|
        parts.append(str(error))
 | 
						|
    else:
 | 
						|
        parts.append(".")
 | 
						|
 | 
						|
    # Spilt the error indication from a helper message (if any)
 | 
						|
    parts[-1] += "\n"
 | 
						|
 | 
						|
    # Suggest useful actions to the user:
 | 
						|
    #  (1) using user site-packages or (2) verifying the permissions
 | 
						|
    if error.errno == errno.EACCES:
 | 
						|
        user_option_part = "Consider using the `--user` option"
 | 
						|
        permissions_part = "Check the permissions"
 | 
						|
 | 
						|
        if not running_under_virtualenv() and not using_user_site:
 | 
						|
            parts.extend(
 | 
						|
                [
 | 
						|
                    user_option_part,
 | 
						|
                    " or ",
 | 
						|
                    permissions_part.lower(),
 | 
						|
                ]
 | 
						|
            )
 | 
						|
        else:
 | 
						|
            parts.append(permissions_part)
 | 
						|
        parts.append(".\n")
 | 
						|
 | 
						|
    # Suggest the user to enable Long Paths if path length is
 | 
						|
    # more than 260
 | 
						|
    if (
 | 
						|
        WINDOWS
 | 
						|
        and error.errno == errno.ENOENT
 | 
						|
        and error.filename
 | 
						|
        and len(error.filename) > 260
 | 
						|
    ):
 | 
						|
        parts.append(
 | 
						|
            "HINT: This error might have occurred since "
 | 
						|
            "this system does not have Windows Long Path "
 | 
						|
            "support enabled. You can find information on "
 | 
						|
            "how to enable this at "
 | 
						|
            "https://pip.pypa.io/warnings/enable-long-paths\n"
 | 
						|
        )
 | 
						|
 | 
						|
    return "".join(parts).strip() + "\n"
 |