direct_url_helpers.py 3.8 KB

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