direct_url_helpers.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. import logging
  2. from pip._internal.models.direct_url import (
  3. DIRECT_URL_METADATA_NAME,
  4. ArchiveInfo,
  5. DirectUrl,
  6. DirectUrlValidationError,
  7. DirInfo,
  8. VcsInfo,
  9. )
  10. from pip._internal.utils.typing import MYPY_CHECK_RUNNING
  11. from pip._internal.vcs import vcs
  12. try:
  13. from json import JSONDecodeError
  14. except ImportError:
  15. # PY2
  16. JSONDecodeError = ValueError # type: ignore
  17. if MYPY_CHECK_RUNNING:
  18. from typing import Optional
  19. from pip._vendor.pkg_resources import Distribution
  20. from pip._internal.models.link import Link
  21. logger = logging.getLogger(__name__)
  22. def direct_url_as_pep440_direct_reference(direct_url, name):
  23. # type: (DirectUrl, str) -> str
  24. """Convert a DirectUrl to a pip requirement string."""
  25. direct_url.validate() # if invalid, this is a pip bug
  26. requirement = name + " @ "
  27. fragments = []
  28. if isinstance(direct_url.info, VcsInfo):
  29. requirement += "{}+{}@{}".format(
  30. direct_url.info.vcs, direct_url.url, direct_url.info.commit_id
  31. )
  32. elif isinstance(direct_url.info, ArchiveInfo):
  33. requirement += direct_url.url
  34. if direct_url.info.hash:
  35. fragments.append(direct_url.info.hash)
  36. else:
  37. assert isinstance(direct_url.info, DirInfo)
  38. requirement += direct_url.url
  39. if direct_url.subdirectory:
  40. fragments.append("subdirectory=" + direct_url.subdirectory)
  41. if fragments:
  42. requirement += "#" + "&".join(fragments)
  43. return requirement
  44. def direct_url_from_link(link, source_dir=None, link_is_in_wheel_cache=False):
  45. # type: (Link, Optional[str], bool) -> DirectUrl
  46. if link.is_vcs:
  47. vcs_backend = vcs.get_backend_for_scheme(link.scheme)
  48. assert vcs_backend
  49. url, requested_revision, _ = (
  50. vcs_backend.get_url_rev_and_auth(link.url_without_fragment)
  51. )
  52. # For VCS links, we need to find out and add commit_id.
  53. if link_is_in_wheel_cache:
  54. # If the requested VCS link corresponds to a cached
  55. # wheel, it means the requested revision was an
  56. # immutable commit hash, otherwise it would not have
  57. # been cached. In that case we don't have a source_dir
  58. # with the VCS checkout.
  59. assert requested_revision
  60. commit_id = requested_revision
  61. else:
  62. # If the wheel was not in cache, it means we have
  63. # had to checkout from VCS to build and we have a source_dir
  64. # which we can inspect to find out the commit id.
  65. assert source_dir
  66. commit_id = vcs_backend.get_revision(source_dir)
  67. return DirectUrl(
  68. url=url,
  69. info=VcsInfo(
  70. vcs=vcs_backend.name,
  71. commit_id=commit_id,
  72. requested_revision=requested_revision,
  73. ),
  74. subdirectory=link.subdirectory_fragment,
  75. )
  76. elif link.is_existing_dir():
  77. return DirectUrl(
  78. url=link.url_without_fragment,
  79. info=DirInfo(),
  80. subdirectory=link.subdirectory_fragment,
  81. )
  82. else:
  83. hash = None
  84. hash_name = link.hash_name
  85. if hash_name:
  86. hash = "{}={}".format(hash_name, link.hash)
  87. return DirectUrl(
  88. url=link.url_without_fragment,
  89. info=ArchiveInfo(hash=hash),
  90. subdirectory=link.subdirectory_fragment,
  91. )
  92. def dist_get_direct_url(dist):
  93. # type: (Distribution) -> Optional[DirectUrl]
  94. """Obtain a DirectUrl from a pkg_resource.Distribution.
  95. Returns None if the distribution has no `direct_url.json` metadata,
  96. or if `direct_url.json` is invalid.
  97. """
  98. if not dist.has_metadata(DIRECT_URL_METADATA_NAME):
  99. return None
  100. try:
  101. return DirectUrl.from_json(dist.get_metadata(DIRECT_URL_METADATA_NAME))
  102. except (
  103. DirectUrlValidationError,
  104. JSONDecodeError,
  105. UnicodeDecodeError
  106. ) as e:
  107. logger.warning(
  108. "Error parsing %s for %s: %s",
  109. DIRECT_URL_METADATA_NAME,
  110. dist.project_name,
  111. e,
  112. )
  113. return None