installer.py 3.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  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