blob: 0754334de9778720440a2b866f12dd11696056ad [file]
"""Utilities for getting an absolute URL from index_url and the URL we find on PyPI index."""
load("//python/private:envsubst.bzl", _envsubst = "envsubst")
def _get_root_directory(url):
scheme_end = url.find("://")
if scheme_end == -1:
fail("Invalid URL format: '{}'".format(url))
scheme = url[:scheme_end]
host_end = url.find("/", scheme_end + 3)
if host_end == -1:
host_end = len(url)
host = url[scheme_end + 3:host_end]
return "{}://{}".format(scheme, host)
def _is_downloadable(url):
"""Checks if the URL would be accepted by the Bazel downloader.
This is based on Bazel's HttpUtils::isUrlSupportedByDownloader
"""
return url.startswith("http://") or url.startswith("https://") or url.startswith("file://")
def _absolute_url(index_url, candidate):
"""Convert into an absolute URL.
Args:
index_url: The index URL where the file has been found.
candidate: The candidate URL which may be not absolute.
Returns:
An absolute URL
"""
if candidate == "":
return candidate
if _is_downloadable(candidate):
return candidate
if candidate.startswith("/"):
# absolute path
root_directory = _get_root_directory(index_url)
return "{}{}".format(root_directory, candidate)
if candidate.startswith(".."):
# relative path with up references
candidate_parts = candidate.split("..")
last = candidate_parts[-1]
for _ in range(len(candidate_parts) - 1):
index_url, _, _ = index_url.rstrip("/").rpartition("/")
return "{}/{}".format(index_url, last.strip("/"))
# relative path without up-references
return "{}/{}".format(index_url.rstrip("/"), candidate)
def _with_envsubst(url, envsubst = None, getenv = None):
if not url or not envsubst or not getenv:
return url
return _envsubst(url, envsubst, getenv)
def _strip_empty_path_segments(url):
"""Removes empty path segments from a URL. Does nothing for urls with no scheme.
Public only for testing.
Args:
url: The url to remove empty path segments from
Returns:
The url with empty path segments removed and any trailing slash preserved.
If the url had no scheme it is returned unchanged.
"""
sep = "://"
scheme, _, rest = url.partition(sep)
if scheme.lower() == "file" and rest.startswith("/"):
sep = sep + "/"
rest = rest[1:]
if rest == "":
return url
stripped = "/".join([p for p in rest.split("/") if p])
if url.endswith("/"):
return "{}{}{}/".format(scheme, sep, stripped)
else:
return "{}{}{}".format(scheme, sep, stripped)
urllib = struct(
is_absolute = _is_downloadable,
# Ensure that we strip empty path segments when making an absolute URL
absolute_url = lambda index_url, candidate, *, envsubst = None, getenv = None: _strip_empty_path_segments(_with_envsubst(_absolute_url(index_url, candidate), envsubst, getenv)),
strip_empty_path_segments = _strip_empty_path_segments,
)