Skip to content

Commit a15a6e8

Browse files
committed
feat: entry_point support for bzlmod hub repos
1 parent 81ddd33 commit a15a6e8

File tree

10 files changed

+188
-104
lines changed

10 files changed

+188
-104
lines changed

examples/bzlmod/MODULE.bazel

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,7 @@ pip.parse(
5757
requirements_lock = "//:requirements_lock_3_10.txt",
5858
requirements_windows = "//:requirements_windows_3_10.txt",
5959
)
60-
61-
# NOTE: The pip_39 repo is only used because the plain `@pip` repo doesn't
62-
# yet support entry points; see https://github.com/bazelbuild/rules_python/issues/1262
63-
use_repo(pip, "pip", "pip_39")
60+
use_repo(pip, "pip")
6461

6562
bazel_dep(name = "other_module", version = "", repo_name = "our_other_module")
6663
local_path_override(

examples/bzlmod/entry_point/BUILD.bazel

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
load("@pip_39//:requirements.bzl", "entry_point")
1+
load("@pip//:requirements.bzl", "entry_point")
22
load("@rules_python//python:defs.bzl", "py_test")
33

44
alias(

python/extensions/pip.bzl

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"pip module extension for use with bzlmod"
1616

1717
load("@pythons_hub//:interpreters.bzl", "DEFAULT_PYTHON_VERSION", "INTERPRETER_LABELS")
18-
load("@rules_python//python/extensions/private:hub_repository.bzl", "hub_repository", "hub_repository_mega")
18+
load("@rules_python//python/extensions/private:hub_repository.bzl", "hub_repository")
1919
load(
2020
"@rules_python//python/pip_install:pip_repository.bzl",
2121
"locked_requirements_label",
@@ -24,6 +24,7 @@ load(
2424
"whl_library",
2525
)
2626
load("@rules_python//python/pip_install:requirements_parser.bzl", parse_requirements = "parse")
27+
load("//python:versions.bzl", "MINOR_MAPPING")
2728

2829
def _create_versioned_pip_and_whl_repos(module_ctx, pip_attr, whl_map):
2930
python_interpreter_target = pip_attr.python_interpreter_target
@@ -55,20 +56,6 @@ def _create_versioned_pip_and_whl_repos(module_ctx, pip_attr, whl_map):
5556
requirements = parse_result.requirements
5657
extra_pip_args = pip_attr.extra_pip_args + parse_result.options
5758

58-
# Create the repository where users load the `requirement` macro. Under bzlmod
59-
# this does not create the install_deps() macro.
60-
# TODO: we may not need this repository once we have entry points
61-
# supported. For now a user can access this repository and use
62-
# the entrypoint functionality.
63-
hub_repository(
64-
name = pip_name,
65-
repo_name = pip_name,
66-
whl_library_alias_names = [
67-
_sanitize_name(name)
68-
for name, _ in parse_result.requirements
69-
],
70-
template = "//python/extensions/private:hub_repository_default.bzl.tmpl",
71-
)
7259
if hub_name not in whl_map:
7360
whl_map[hub_name] = {}
7461

@@ -96,7 +83,7 @@ def _create_versioned_pip_and_whl_repos(module_ctx, pip_attr, whl_map):
9683
if whl_name not in whl_map[hub_name]:
9784
whl_map[hub_name][whl_name] = []
9885

99-
whl_map[hub_name][whl_name].append(pip_attr.python_version)
86+
whl_map[hub_name][whl_name].append(MINOR_MAPPING.get(pip_attr.python_version, pip_attr.python_version))
10087

10188
def _pip_impl(module_ctx):
10289
"""Implementation of a class tag that creates the pip hub(s) and corresponding pip spoke, alias and whl repositories.
@@ -229,7 +216,7 @@ def _pip_impl(module_ctx):
229216

230217
for hub_name, whl_map in hub_whl_map.items():
231218
# Create the hub repository for pip.
232-
hub_repository_mega(
219+
hub_repository(
233220
name = hub_name,
234221
repo_name = hub_name,
235222
whl_map = whl_map,

python/extensions/private/hub_repository.bzl

Lines changed: 17 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
"A hub_repository rule used to create bzlmod hub repos."
1616

17+
load("//python:versions.bzl", "MINOR_MAPPING")
1718
load("//python/private:create_pkg_aliases.bzl", "create_pkg_aliases")
1819

1920
_BUILD_FILE_CONTENTS = """\
@@ -24,54 +25,10 @@ exports_files(["requirements.bzl"])
2425
"""
2526

2627
def _impl(rctx):
27-
bzl_packages = rctx.attr.whl_library_alias_names
28-
repo_name = rctx.attr.repo_name
29-
build_contents = _BUILD_FILE_CONTENTS
30-
create_pkg_aliases(rctx, repo_name, bzl_packages)
31-
32-
# NOTE: we are using the canonical name with the double '@' in order to
33-
# always uniquely identify a repository, as the labels are being passed as
34-
# a string and the resolution of the label happens at the call-site of the
35-
# `requirement`, et al. macros.
36-
macro_tmpl = "@@{name}//{{}}:{{}}".format(name = rctx.attr.name)
37-
38-
rctx.file("BUILD.bazel", build_contents)
39-
rctx.template("requirements.bzl", rctx.attr.template, substitutions = {
40-
"%%ALL_REQUIREMENTS%%": repr([
41-
macro_tmpl.format(p, p)
42-
for p in bzl_packages
43-
]),
44-
"%%ALL_WHL_REQUIREMENTS%%": repr([
45-
macro_tmpl.format(p, "whl")
46-
for p in bzl_packages
47-
]),
48-
"%%MACRO_TMPL%%": macro_tmpl,
49-
"%%NAME%%": rctx.attr.name,
50-
})
51-
52-
hub_repository = repository_rule(
53-
attrs = {
54-
"repo_name": attr.string(
55-
mandatory = True,
56-
doc = "The apparent name of the repo. This is needed because in bzlmod, the name attribute becomes the canonical name.",
57-
),
58-
"template": attr.label(
59-
mandatory = True,
60-
doc = "The template to use to create a hub repo",
61-
),
62-
"whl_library_alias_names": attr.string_list(
63-
mandatory = True,
64-
doc = "The list of whl alias that we use to build aliases and the whl names",
65-
),
66-
},
67-
doc = """A rule for creating bzlmod hub repo. PRIVATE USE ONLY.""",
68-
implementation = _impl,
69-
)
70-
71-
def _impl_2(rctx):
7228
bzl_packages = rctx.attr.whl_map.keys()
7329
repo_name = rctx.attr.repo_name
7430
build_contents = _BUILD_FILE_CONTENTS
31+
7532
create_pkg_aliases(
7633
rctx = rctx,
7734
repo_name = repo_name,
@@ -92,6 +49,7 @@ def _impl_2(rctx):
9249
# a string and the resolution of the label happens at the call-site of the
9350
# `requirement`, et al. macros.
9451
macro_tmpl = "@@{name}//{{}}:{{}}".format(name = rctx.attr.name)
52+
entrypoint_tmpl = "@@{name}_{{version_label}}_{{pkg}}//:rules_python_wheel_entry_point_{{script}}".format(name = rctx.attr.name)
9553

9654
rctx.file("BUILD.bazel", build_contents)
9755
rctx.template("requirements.bzl", rctx.attr._template, substitutions = {
@@ -103,11 +61,21 @@ def _impl_2(rctx):
10361
macro_tmpl.format(p, "whl")
10462
for p in bzl_packages
10563
]),
64+
"%%DEFAULT_PY_VERSION%%": repr(MINOR_MAPPING.get(rctx.attr.default_version, rctx.attr.default_version)),
65+
"%%ENTRYPOINT_TMPL%%": entrypoint_tmpl,
10666
"%%MACRO_TMPL%%": macro_tmpl,
10767
"%%NAME%%": rctx.attr.name,
68+
"%%PACKAGE_AVAILABILITY%%": repr({
69+
k: [
70+
MINOR_MAPPING.get(v, v)
71+
for v in versions
72+
]
73+
for k, versions in rctx.attr.whl_map.items()
74+
}),
75+
"%%RULES_PYTHON%%": rctx.attr._template.workspace_name,
10876
})
10977

110-
hub_repository_mega = repository_rule(
78+
hub_repository = repository_rule(
11179
attrs = {
11280
"default_version": attr.string(
11381
mandatory = True,
@@ -118,13 +86,13 @@ hub_repository_mega = repository_rule(
11886
),
11987
"whl_map": attr.string_list_dict(
12088
mandatory = True,
121-
doc = "The wheel map where values are json strings encoding dicts.",
89+
doc = "The wheel map where values are python versions",
12290
),
12391
"_template": attr.label(
12492
doc = "The template to use to create a hub repo",
125-
default = ":hub_repository_version.bzl.tmpl",
93+
default = ":requirements.bzl.tmpl",
12694
),
12795
},
12896
doc = """A rule for creating bzlmod hub repo. PRIVATE USE ONLY.""",
129-
implementation = _impl_2,
97+
implementation = _impl,
13098
)

python/extensions/private/hub_repository_default.bzl.tmpl

Lines changed: 0 additions & 30 deletions
This file was deleted.

python/extensions/private/hub_repository_version.bzl.tmpl renamed to python/extensions/private/requirements.bzl.tmpl

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,15 @@ This file is different from the other bzlmod template
66
because we do not support entry_point yet.
77
"""
88

9+
load("@@%%RULES_PYTHON%%//python/pip_install:entrypoint.bzl", _entry_point = "entry_point")
10+
911
all_requirements = %%ALL_REQUIREMENTS%%
1012

1113
all_whl_requirements = %%ALL_WHL_REQUIREMENTS%%
1214

15+
_default_py_version = %%DEFAULT_PY_VERSION%%
16+
_packages = %%PACKAGE_AVAILABILITY%%
17+
1318
def _clean_name(name):
1419
return name.replace("-", "_").replace(".", "_").lower()
1520

@@ -28,5 +33,14 @@ def dist_info_requirement(name):
2833
def entry_point(pkg, script = None):
2934
"""entry_point returns the target of the canonical label of the package entrypoints.
3035
"""
31-
# TODO: https://github.com/bazelbuild/rules_python/issues/1262
32-
print("not implemented")
36+
selects = _entry_point(
37+
tmpl = "%%ENTRYPOINT_TMPL%%",
38+
pkg = _clean_name(pkg),
39+
script = script,
40+
packages = _packages,
41+
default_version = _default_py_version,
42+
)
43+
if selects == None:
44+
fail("Package '{}' does not exist, select one from: {}".format(pkg, _packages.keys()))
45+
46+
return select(selects)

python/pip_install/entrypoint.bzl

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Copyright 2023 The Bazel Authors. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"entry_point macro implementation for bzlmod. PRIVATE ONLY."
16+
17+
def entry_point(*, pkg, packages, default_version, tmpl, script = None):
18+
"""Return an entry_point script.
19+
20+
Args:
21+
pkg: the package name.
22+
script: the script name to use, defaults to pkg name.
23+
packages: the dictionary of packages to python versions that are supported.
24+
default_version: the default python version.
25+
tmpl: the template that will be interpolated by this function. The
26+
following keys are going to be replaced: 'version_label', 'pkg' and
27+
'script'.
28+
29+
Returns:
30+
A dict that can be used in select statement or None if the pkg is not
31+
in the supplied packages dictionary.
32+
"""
33+
if not script:
34+
script = pkg
35+
36+
if pkg not in packages:
37+
return None
38+
39+
selects = {}
40+
default = ""
41+
for full_version in packages[pkg]:
42+
condition = str(Label("//python/config_settings:is_python_{}".format(full_version)))
43+
44+
entry_point = tmpl.format(
45+
version_label = full_version.rpartition(".")[0].replace(".", ""),
46+
pkg = pkg,
47+
script = script,
48+
)
49+
50+
if full_version == default_version:
51+
default = entry_point
52+
else:
53+
selects[condition] = entry_point
54+
55+
if default:
56+
selects["//conditions:default"] = default
57+
58+
return selects

python/pip_install/private/test/BUILD.bazel

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
load("@bazel_skylib//rules:diff_test.bzl", "diff_test")
2+
load(":entrypoint_test.bzl", "entry_point_tests")
23
load(":requirements_parser_tests.bzl", parse_requirements_tests = "parse_tests")
34

45
diff_test(
@@ -18,3 +19,5 @@ diff_test(
1819
)
1920

2021
parse_requirements_tests(name = "test_parse_requirements")
22+
23+
entry_point_tests(name = "test_entry_point")

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy