legacy.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. """Legacy installation process, i.e. `setup.py install`.
  2. """
  3. import logging
  4. import os
  5. from distutils.util import change_root
  6. from typing import List, Optional, Sequence
  7. from pip._internal.build_env import BuildEnvironment
  8. from pip._internal.exceptions import InstallationError
  9. from pip._internal.models.scheme import Scheme
  10. from pip._internal.utils.logging import indent_log
  11. from pip._internal.utils.misc import ensure_dir
  12. from pip._internal.utils.setuptools_build import make_setuptools_install_args
  13. from pip._internal.utils.subprocess import runner_with_spinner_message
  14. from pip._internal.utils.temp_dir import TempDirectory
  15. logger = logging.getLogger(__name__)
  16. class LegacyInstallFailure(Exception):
  17. pass
  18. def write_installed_files_from_setuptools_record(
  19. record_lines: List[str],
  20. root: Optional[str],
  21. req_description: str,
  22. ) -> None:
  23. def prepend_root(path: str) -> str:
  24. if root is None or not os.path.isabs(path):
  25. return path
  26. else:
  27. return change_root(root, path)
  28. for line in record_lines:
  29. directory = os.path.dirname(line)
  30. if directory.endswith(".egg-info"):
  31. egg_info_dir = prepend_root(directory)
  32. break
  33. else:
  34. message = (
  35. "{} did not indicate that it installed an "
  36. ".egg-info directory. Only setup.py projects "
  37. "generating .egg-info directories are supported."
  38. ).format(req_description)
  39. raise InstallationError(message)
  40. new_lines = []
  41. for line in record_lines:
  42. filename = line.strip()
  43. if os.path.isdir(filename):
  44. filename += os.path.sep
  45. new_lines.append(os.path.relpath(prepend_root(filename), egg_info_dir))
  46. new_lines.sort()
  47. ensure_dir(egg_info_dir)
  48. inst_files_path = os.path.join(egg_info_dir, "installed-files.txt")
  49. with open(inst_files_path, "w") as f:
  50. f.write("\n".join(new_lines) + "\n")
  51. def install(
  52. install_options: List[str],
  53. global_options: Sequence[str],
  54. root: Optional[str],
  55. home: Optional[str],
  56. prefix: Optional[str],
  57. use_user_site: bool,
  58. pycompile: bool,
  59. scheme: Scheme,
  60. setup_py_path: str,
  61. isolated: bool,
  62. req_name: str,
  63. build_env: BuildEnvironment,
  64. unpacked_source_directory: str,
  65. req_description: str,
  66. ) -> bool:
  67. header_dir = scheme.headers
  68. with TempDirectory(kind="record") as temp_dir:
  69. try:
  70. record_filename = os.path.join(temp_dir.path, "install-record.txt")
  71. install_args = make_setuptools_install_args(
  72. setup_py_path,
  73. global_options=global_options,
  74. install_options=install_options,
  75. record_filename=record_filename,
  76. root=root,
  77. prefix=prefix,
  78. header_dir=header_dir,
  79. home=home,
  80. use_user_site=use_user_site,
  81. no_user_config=isolated,
  82. pycompile=pycompile,
  83. )
  84. runner = runner_with_spinner_message(
  85. f"Running setup.py install for {req_name}"
  86. )
  87. with indent_log(), build_env:
  88. runner(
  89. cmd=install_args,
  90. cwd=unpacked_source_directory,
  91. )
  92. if not os.path.exists(record_filename):
  93. logger.debug("Record file %s not found", record_filename)
  94. # Signal to the caller that we didn't install the new package
  95. return False
  96. except Exception as e:
  97. # Signal to the caller that we didn't install the new package
  98. raise LegacyInstallFailure from e
  99. # At this point, we have successfully installed the requirement.
  100. # We intentionally do not use any encoding to read the file because
  101. # setuptools writes the file using distutils.file_util.write_file,
  102. # which does not specify an encoding.
  103. with open(record_filename) as f:
  104. record_lines = f.read().splitlines()
  105. write_installed_files_from_setuptools_record(record_lines, root, req_description)
  106. return True