installer.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. import glob
  2. import os
  3. import subprocess
  4. import sys
  5. import tempfile
  6. import warnings
  7. from distutils import log
  8. from distutils.errors import DistutilsError
  9. import pkg_resources
  10. from setuptools.wheel import Wheel
  11. from ._deprecation_warning import SetuptoolsDeprecationWarning
  12. def _fixup_find_links(find_links):
  13. """Ensure find-links option end-up being a list of strings."""
  14. if isinstance(find_links, str):
  15. return find_links.split()
  16. assert isinstance(find_links, (tuple, list))
  17. return find_links
  18. def fetch_build_egg(dist, req): # noqa: C901 # is too complex (16) # FIXME
  19. """Fetch an egg needed for building.
  20. Use pip/wheel to fetch/build a wheel."""
  21. warnings.warn(
  22. "setuptools.installer is deprecated. Requirements should "
  23. "be satisfied by a PEP 517 installer.",
  24. SetuptoolsDeprecationWarning,
  25. )
  26. # Warn if wheel is not available
  27. try:
  28. pkg_resources.get_distribution('wheel')
  29. except pkg_resources.DistributionNotFound:
  30. dist.announce('WARNING: The wheel package is not available.', log.WARN)
  31. # Ignore environment markers; if supplied, it is required.
  32. req = strip_marker(req)
  33. # Take easy_install options into account, but do not override relevant
  34. # pip environment variables (like PIP_INDEX_URL or PIP_QUIET); they'll
  35. # take precedence.
  36. opts = dist.get_option_dict('easy_install')
  37. if 'allow_hosts' in opts:
  38. raise DistutilsError('the `allow-hosts` option is not supported '
  39. 'when using pip to install requirements.')
  40. quiet = 'PIP_QUIET' not in os.environ and 'PIP_VERBOSE' not in os.environ
  41. if 'PIP_INDEX_URL' in os.environ:
  42. index_url = None
  43. elif 'index_url' in opts:
  44. index_url = opts['index_url'][1]
  45. else:
  46. index_url = None
  47. find_links = (
  48. _fixup_find_links(opts['find_links'][1])[:] if 'find_links' in opts
  49. else []
  50. )
  51. if dist.dependency_links:
  52. find_links.extend(dist.dependency_links)
  53. eggs_dir = os.path.realpath(dist.get_egg_cache_dir())
  54. environment = pkg_resources.Environment()
  55. for egg_dist in pkg_resources.find_distributions(eggs_dir):
  56. if egg_dist in req and environment.can_add(egg_dist):
  57. return egg_dist
  58. with tempfile.TemporaryDirectory() as tmpdir:
  59. cmd = [
  60. sys.executable, '-m', 'pip',
  61. '--disable-pip-version-check',
  62. 'wheel', '--no-deps',
  63. '-w', tmpdir,
  64. ]
  65. if quiet:
  66. cmd.append('--quiet')
  67. if index_url is not None:
  68. cmd.extend(('--index-url', index_url))
  69. for link in find_links or []:
  70. cmd.extend(('--find-links', link))
  71. # If requirement is a PEP 508 direct URL, directly pass
  72. # the URL to pip, as `req @ url` does not work on the
  73. # command line.
  74. cmd.append(req.url or str(req))
  75. try:
  76. subprocess.check_call(cmd)
  77. except subprocess.CalledProcessError as e:
  78. raise DistutilsError(str(e)) from e
  79. wheel = Wheel(glob.glob(os.path.join(tmpdir, '*.whl'))[0])
  80. dist_location = os.path.join(eggs_dir, wheel.egg_name())
  81. wheel.install_as_egg(dist_location)
  82. dist_metadata = pkg_resources.PathMetadata(
  83. dist_location, os.path.join(dist_location, 'EGG-INFO'))
  84. dist = pkg_resources.Distribution.from_filename(
  85. dist_location, metadata=dist_metadata)
  86. return dist
  87. def strip_marker(req):
  88. """
  89. Return a new requirement without the environment marker to avoid
  90. calling pip with something like `babel; extra == "i18n"`, which
  91. would always be ignored.
  92. """
  93. # create a copy to avoid mutating the input
  94. req = pkg_resources.Requirement.parse(str(req))
  95. req.marker = None
  96. return req