"""Legacy installation process, i.e. `setup.py install`. """ import logging import os from distutils.util import change_root from typing import List, Optional, Sequence from pip._internal.build_env import BuildEnvironment from pip._internal.exceptions import InstallationError from pip._internal.models.scheme import Scheme from pip._internal.utils.logging import indent_log from pip._internal.utils.misc import ensure_dir from pip._internal.utils.setuptools_build import make_setuptools_install_args from pip._internal.utils.subprocess import runner_with_spinner_message from pip._internal.utils.temp_dir import TempDirectory logger = logging.getLogger(__name__) class LegacyInstallFailure(Exception): pass def write_installed_files_from_setuptools_record( record_lines: List[str], root: Optional[str], req_description: str, ) -> None: def prepend_root(path: str) -> str: if root is None or not os.path.isabs(path): return path else: return change_root(root, path) for line in record_lines: directory = os.path.dirname(line) if directory.endswith(".egg-info"): egg_info_dir = prepend_root(directory) break else: message = ( "{} did not indicate that it installed an " ".egg-info directory. Only setup.py projects " "generating .egg-info directories are supported." ).format(req_description) raise InstallationError(message) new_lines = [] for line in record_lines: filename = line.strip() if os.path.isdir(filename): filename += os.path.sep new_lines.append(os.path.relpath(prepend_root(filename), egg_info_dir)) new_lines.sort() ensure_dir(egg_info_dir) inst_files_path = os.path.join(egg_info_dir, "installed-files.txt") with open(inst_files_path, "w") as f: f.write("\n".join(new_lines) + "\n") def install( install_options: List[str], global_options: Sequence[str], root: Optional[str], home: Optional[str], prefix: Optional[str], use_user_site: bool, pycompile: bool, scheme: Scheme, setup_py_path: str, isolated: bool, req_name: str, build_env: BuildEnvironment, unpacked_source_directory: str, req_description: str, ) -> bool: header_dir = scheme.headers with TempDirectory(kind="record") as temp_dir: try: record_filename = os.path.join(temp_dir.path, "install-record.txt") install_args = make_setuptools_install_args( setup_py_path, global_options=global_options, install_options=install_options, record_filename=record_filename, root=root, prefix=prefix, header_dir=header_dir, home=home, use_user_site=use_user_site, no_user_config=isolated, pycompile=pycompile, ) runner = runner_with_spinner_message( f"Running setup.py install for {req_name}" ) with indent_log(), build_env: runner( cmd=install_args, cwd=unpacked_source_directory, ) if not os.path.exists(record_filename): logger.debug("Record file %s not found", record_filename) # Signal to the caller that we didn't install the new package return False except Exception as e: # Signal to the caller that we didn't install the new package raise LegacyInstallFailure from e # At this point, we have successfully installed the requirement. # We intentionally do not use any encoding to read the file because # setuptools writes the file using distutils.file_util.write_file, # which does not specify an encoding. with open(record_filename) as f: record_lines = f.read().splitlines() write_installed_files_from_setuptools_record(record_lines, root, req_description) return True