diff --git a/python/private/BUILD.bazel b/python/private/BUILD.bazel index 765f6e3e04..24b4840266 100644 --- a/python/private/BUILD.bazel +++ b/python/private/BUILD.bazel @@ -153,6 +153,15 @@ bzl_library( ], ) +bzl_library( + name = "pip_repo_name_bzl", + srcs = ["pip_repo_name.bzl"], + deps = [ + ":normalize_name_bzl", + ":parse_whl_name_bzl", + ], +) + bzl_library( name = "pypi_index_bzl", srcs = ["pypi_index.bzl"], diff --git a/python/private/bzlmod/BUILD.bazel b/python/private/bzlmod/BUILD.bazel index 2eab575726..3362f34ffd 100644 --- a/python/private/bzlmod/BUILD.bazel +++ b/python/private/bzlmod/BUILD.bazel @@ -36,6 +36,7 @@ bzl_library( "//python/private:normalize_name_bzl", "//python/private:parse_requirements_bzl", "//python/private:parse_whl_name_bzl", + "//python/private:pip_repo_name_bzl", "//python/private:version_label_bzl", ":bazel_features_bzl", ] + [ diff --git a/python/private/bzlmod/pip.bzl b/python/private/bzlmod/pip.bzl index f4a19d6c56..75dbc47505 100644 --- a/python/private/bzlmod/pip.bzl +++ b/python/private/bzlmod/pip.bzl @@ -26,6 +26,7 @@ load("//python/private:auth.bzl", "AUTH_ATTRS") load("//python/private:normalize_name.bzl", "normalize_name") load("//python/private:parse_requirements.bzl", "host_platform", "parse_requirements", "select_requirement") load("//python/private:parse_whl_name.bzl", "parse_whl_name") +load("//python/private:pip_repo_name.bzl", "pip_repo_name") load("//python/private:pypi_index.bzl", "simpleapi_download") load("//python/private:render_pkg_aliases.bzl", "whl_alias") load("//python/private:repo_utils.bzl", "repo_utils") @@ -260,7 +261,7 @@ def _create_whl_repos(module_ctx, pip_attr, whl_map, whl_overrides, group_map, s # This is no-op because pip is not used to download the wheel. whl_library_args.pop("download_only", None) - repo_name = _repo_name(pip_name, distribution.filename) + repo_name = pip_repo_name(pip_name, distribution.filename, distribution.sha256) whl_library_args["requirement"] = requirement.srcs.requirement whl_library_args["urls"] = [distribution.url] whl_library_args["sha256"] = distribution.sha256 @@ -317,21 +318,6 @@ def _create_whl_repos(module_ctx, pip_attr, whl_map, whl_overrides, group_map, s ), ) -def _repo_name(prefix, filename): - if not filename.endswith(".whl"): - # Then the filename is basically foo-3.2.1. - name = normalize_name(filename) - else: - parsed = parse_whl_name(filename) - name = "{}_{}_{}_{}_{}".format( - parsed.distribution, - parsed.version, - parsed.python_tag, - parsed.abi_tag, - parsed.platform_tag, - ) - return "{}__{}".format(prefix, normalize_name(name)) - def _pip_impl(module_ctx): """Implementation of a class tag that creates the pip hub and corresponding pip spoke whl repositories. diff --git a/python/private/pip_repo_name.bzl b/python/private/pip_repo_name.bzl new file mode 100644 index 0000000000..8661dc68ca --- /dev/null +++ b/python/private/pip_repo_name.bzl @@ -0,0 +1,39 @@ +# Copyright 2024 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""A function to convert a dist name to a valid bazel repo name. +""" + +load(":normalize_name.bzl", "normalize_name") +load(":parse_whl_name.bzl", "parse_whl_name") + +def pip_repo_name(prefix, filename, sha256): + """Return a valid whl_library repo name given a distribution filename. + + Args: + prefix: str, the prefix of the whl_library. + filename: str, the filename of the distribution. + sha256: str, the sha256 of the distribution. + + Returns: + a string that can be used in `whl_library`. + """ + if not filename.endswith(".whl"): + # Then the filename is basically foo-3.2.1. + name = normalize_name(filename.rpartition("-")[0]) + else: + parsed = parse_whl_name(filename) + name = normalize_name(parsed.distribution) + + return "{}__{}_{}".format(prefix, name, sha256[:8]) diff --git a/tests/private/pip_repo_name/BUILD.bazel b/tests/private/pip_repo_name/BUILD.bazel new file mode 100644 index 0000000000..7c6782daaf --- /dev/null +++ b/tests/private/pip_repo_name/BUILD.bazel @@ -0,0 +1,3 @@ +load(":pip_repo_name_tests.bzl", "pip_repo_name_test_suite") + +pip_repo_name_test_suite(name = "pip_repo_name_tests") diff --git a/tests/private/pip_repo_name/pip_repo_name_tests.bzl b/tests/private/pip_repo_name/pip_repo_name_tests.bzl new file mode 100644 index 0000000000..cde4475aa2 --- /dev/null +++ b/tests/private/pip_repo_name/pip_repo_name_tests.bzl @@ -0,0 +1,52 @@ +# Copyright 2023 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"" + +load("@rules_testing//lib:test_suite.bzl", "test_suite") +load("//python/private:pip_repo_name.bzl", "pip_repo_name") # buildifier: disable=bzl-visibility + +_tests = [] + +def _test_simple(env): + got = pip_repo_name("prefix", "foo-1.2.3-py3-none-any.whl", "deadbeef") + env.expect.that_str(got).equals("prefix__foo_deadbeef") + +_tests.append(_test_simple) + +def _test_sdist(env): + got = pip_repo_name("prefix", "foo-1.2.3.tar.gz", "deadbeef000deadbeef") + env.expect.that_str(got).equals("prefix__foo_deadbeef") + +_tests.append(_test_sdist) + +def _test_platform_whl(env): + got = pip_repo_name( + "prefix", + "foo-1.2.3-cp39.cp310-abi3-manylinux1_x86_64.manylinux_2_17_x86_64.whl", + "deadbeef000deadbeef", + ) + + # We only need the first segment of each + env.expect.that_str(got).equals("prefix__foo_deadbeef") + +_tests.append(_test_platform_whl) + +def pip_repo_name_test_suite(name): + """Create the test suite. + + Args: + name: the name of the test suite + """ + test_suite(name = name, basic_tests = _tests)