You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

98 lines
3.5KB

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