legacy.py 4.3 KB

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