diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e59564b94..59dfbe481b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ A brief description of the categories of changes: * (gazelle): Fix incorrect use of `t.Fatal`/`t.Fatalf` in tests. * (toolchain) Omit third-party python packages from coverage reports from stage2 bootstrap template. +* (bzlmod) Properly handle relative path URLs in parse_simpleapi_html.bzl ### Added * Nothing yet diff --git a/examples/bzlmod/MODULE.bazel.lock b/examples/bzlmod/MODULE.bazel.lock index 4deef01be4..8d02256e95 100644 --- a/examples/bzlmod/MODULE.bazel.lock +++ b/examples/bzlmod/MODULE.bazel.lock @@ -1231,7 +1231,7 @@ }, "@@rules_python~//python/extensions:pip.bzl%pip": { "general": { - "bzlTransitiveDigest": "eWzi4IV0AzQ+cpVkMkdU/wOc7BUQdo0hAAQcCh8C4uU=", + "bzlTransitiveDigest": "9hiLCuWaaaU7Q+l2ONVr1A0NcG1JfSihv1UYeA1SpNY=", "usagesDigest": "MChlcSw99EuW3K7OOoMcXQIdcJnEh6YmfyjJm+9mxIg=", "recordedFileInputs": { "@@other_module~//requirements_lock_3_11.txt": "a7d0061366569043d5efcf80e34a32c732679367cb3c831c4cdc606adc36d314", @@ -6140,7 +6140,7 @@ }, "@@rules_python~//python/private/pypi:pip.bzl%pip_internal": { "general": { - "bzlTransitiveDigest": "RyEJxfGmNQVzqInjjGrR29yqfFPKe9DKgODI1mxd8wA=", + "bzlTransitiveDigest": "VoK/T0JkBdcomCHnDIYkX+stkywdxrh1MVM16e8D4sE=", "usagesDigest": "Y8ihY+R57BAFhalrVLVdJFrpwlbsiKz9JPJ99ljF7HA=", "recordedFileInputs": { "@@rules_python~//tools/publish/requirements.txt": "031e35d03dde03ae6305fe4b3d1f58ad7bdad857379752deede0f93649991b8a", diff --git a/python/private/pypi/parse_simpleapi_html.bzl b/python/private/pypi/parse_simpleapi_html.bzl index 81ee385f86..b4e7dd8330 100644 --- a/python/private/pypi/parse_simpleapi_html.bzl +++ b/python/private/pypi/parse_simpleapi_html.bzl @@ -78,7 +78,7 @@ def parse_simpleapi_html(*, url, content): url = _absolute_url(url, dist_url), sha256 = sha256, metadata_sha256 = metadata_sha256, - metadata_url = _absolute_url(url, metadata_url), + metadata_url = _absolute_url(url, metadata_url) if metadata_url else "", yanked = yanked, ) else: @@ -109,18 +109,33 @@ def _get_root_directory(url): 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): + if candidate == "": + return candidate + + if _is_downloadable(candidate): + return candidate + if candidate.startswith("/"): - # absolute url + # absolute path root_directory = _get_root_directory(index_url) return "{}{}".format(root_directory, candidate) - if not candidate.startswith(".."): - return 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("/") - 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("/")) - return "{}/{}".format(index_url, last.strip("/")) + # relative path without up-references + return "{}/{}".format(index_url, candidate) diff --git a/tests/pypi/parse_simpleapi_html/parse_simpleapi_html_tests.bzl b/tests/pypi/parse_simpleapi_html/parse_simpleapi_html_tests.bzl index aa735b83d8..d3c42a8864 100644 --- a/tests/pypi/parse_simpleapi_html/parse_simpleapi_html_tests.bzl +++ b/tests/pypi/parse_simpleapi_html/parse_simpleapi_html_tests.bzl @@ -255,6 +255,40 @@ def _test_whls(env): yanked = False, ), ), + ( + struct( + attrs = [ + 'href="1.0.0/mypy_extensions-1.0.0-py3-none-any.whl#sha256=deadbeef"', + ], + filename = "mypy_extensions-1.0.0-py3-none-any.whl", + url = "https://example.org/simple/mypy_extensions", + ), + struct( + filename = "mypy_extensions-1.0.0-py3-none-any.whl", + metadata_sha256 = "", + metadata_url = "", + sha256 = "deadbeef", + url = "https://example.org/simple/mypy_extensions/1.0.0/mypy_extensions-1.0.0-py3-none-any.whl", + yanked = False, + ), + ), + ( + struct( + attrs = [ + 'href="unknown://example.com/mypy_extensions-1.0.0-py3-none-any.whl#sha256=deadbeef"', + ], + filename = "mypy_extensions-1.0.0-py3-none-any.whl", + url = "https://example.org/simple/mypy_extensions", + ), + struct( + filename = "mypy_extensions-1.0.0-py3-none-any.whl", + metadata_sha256 = "", + metadata_url = "", + sha256 = "deadbeef", + url = "https://example.org/simple/mypy_extensions/unknown://example.com/mypy_extensions-1.0.0-py3-none-any.whl", + yanked = False, + ), + ), ] for (input, want) in tests: