diff --git a/.bazelrc b/.bazelrc
index 87fa6d530..3a5497a07 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -3,8 +3,8 @@
# This lets us glob() up all the files inside the examples to make them inputs to tests
# (Note, we cannot use `common --deleted_packages` because the bazel version command doesn't support it)
# To update these lines, run tools/bazel_integration_test/update_deleted_packages.sh
-build --deleted_packages=examples/build_file_generation,examples/build_file_generation/random_number_generator,examples/bzlmod,examples/bzlmod/entry_point,examples/bzlmod/libs/my_lib,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/runfiles,examples/bzlmod/tests,examples/bzlmod/whl_mods,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_install,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_proto_library,tests/compile_pip_requirements,tests/compile_pip_requirements_test_from_external_workspace,tests/ignore_root_user_error,tests/pip_repository_entry_points
-query --deleted_packages=examples/build_file_generation,examples/build_file_generation/random_number_generator,examples/bzlmod,examples/bzlmod/entry_point,examples/bzlmod/libs/my_lib,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/runfiles,examples/bzlmod/tests,examples/bzlmod/whl_mods,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_install,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_proto_library,tests/compile_pip_requirements,tests/compile_pip_requirements_test_from_external_workspace,tests/ignore_root_user_error,tests/pip_repository_entry_points
+build --deleted_packages=examples/build_file_generation,examples/build_file_generation/random_number_generator,examples/bzlmod,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/bzlmod/entry_point,examples/bzlmod/libs/my_lib,examples/bzlmod/other_module,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/runfiles,examples/bzlmod/tests,examples/bzlmod/tests/other_module,examples/bzlmod/whl_mods,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_install,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_proto_library,tests/compile_pip_requirements,tests/compile_pip_requirements_test_from_external_workspace,tests/ignore_root_user_error,tests/pip_repository_entry_points
+query --deleted_packages=examples/build_file_generation,examples/build_file_generation/random_number_generator,examples/bzlmod,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/bzlmod/entry_point,examples/bzlmod/libs/my_lib,examples/bzlmod/other_module,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/runfiles,examples/bzlmod/tests,examples/bzlmod/tests/other_module,examples/bzlmod/whl_mods,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_install,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_proto_library,tests/compile_pip_requirements,tests/compile_pip_requirements_test_from_external_workspace,tests/ignore_root_user_error,tests/pip_repository_entry_points
test --test_output=errors
diff --git a/docs/pip.md b/docs/pip.md
index 6b96607bc..b3ad331bb 100644
--- a/docs/pip.md
+++ b/docs/pip.md
@@ -18,7 +18,7 @@ whl_library_alias(name, name | A unique name for this repository. | Name | required | |
-| default_version | - | String | required | |
+| default_version | Optional Python version in major.minor format, e.g. '3.10'.The Python version of the wheel to use when the versions from version_map
don't match. This allows the default (version unaware) rules to match and select a wheel. If not specified, then the default rules won't be able to resolve a wheel and an error will occur. | String | optional | ""
|
| repo_mapping | A dictionary from local repository name to global repository name. This allows controls over workspace dependency resolution for dependencies of this repository.<p>For example, an entry "@foo": "@bar"
declares that, for any time this repository depends on @foo
(such as a dependency on @foo//some:target
, it should actually resolve that dependency within globally-declared @bar
(@bar//some:target
). | Dictionary: String -> String | required | |
| version_map | - | Dictionary: String -> String | required | |
| wheel_name | - | String | required | |
diff --git a/examples/bzlmod/other_module/BUILD.bazel b/examples/bzlmod/other_module/BUILD.bazel
new file mode 100644
index 000000000..d50a3a09d
--- /dev/null
+++ b/examples/bzlmod/other_module/BUILD.bazel
@@ -0,0 +1,9 @@
+load("@python_versions//3.11:defs.bzl", compile_pip_requirements_311 = "compile_pip_requirements")
+
+# NOTE: To update the requirements, you need to uncomment the rules_python
+# override in the MODULE.bazel.
+compile_pip_requirements_311(
+ name = "requirements",
+ requirements_in = "requirements.in",
+ requirements_txt = "requirements_lock_3_11.txt",
+)
diff --git a/examples/bzlmod/other_module/MODULE.bazel b/examples/bzlmod/other_module/MODULE.bazel
index cc23a5160..959501abc 100644
--- a/examples/bzlmod/other_module/MODULE.bazel
+++ b/examples/bzlmod/other_module/MODULE.bazel
@@ -6,10 +6,20 @@ module(
# that the parent module uses.
bazel_dep(name = "rules_python", version = "")
-# It is not best practice to use a python.toolchian in
-# a submodule. This code only exists to test that
-# we support doing this. This code is only for rules_python
-# testing purposes.
+# The story behind this commented out override:
+# This override is necessary to generate/update the requirements file
+# for this module. This is because running it via the outer
+# module doesn't work -- the `requirements.update` target can't find
+# the correct file to update.
+# Running in the submodule itself works, but submodules using overrides
+# is considered an error until Bazel 6.3, which prevents the outer module
+# from depending on this module.
+# So until 6.3 and higher is the minimum, we leave this commented out.
+# local_path_override(
+# module_name = "rules_python",
+# path = "../../..",
+# )
+
PYTHON_NAME_39 = "python_3_9"
PYTHON_NAME_311 = "python_3_11"
@@ -29,6 +39,20 @@ python.toolchain(
# created by the above python.toolchain calls.
use_repo(
python,
+ "python_versions",
PYTHON_NAME_39,
PYTHON_NAME_311,
)
+
+pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip")
+pip.parse(
+ hub_name = "other_module_pip",
+ # NOTE: This version must be different than the root module's
+ # default python version.
+ # This is testing that a sub-module can use pip.parse() and only specify
+ # Python versions that DON'T include whatever the root-module's default
+ # Python version is.
+ python_version = "3.11",
+ requirements_lock = ":requirements_lock_3_11.txt",
+)
+use_repo(pip, "other_module_pip")
diff --git a/examples/bzlmod/other_module/other_module/pkg/BUILD.bazel b/examples/bzlmod/other_module/other_module/pkg/BUILD.bazel
index 6e37df823..021c96980 100644
--- a/examples/bzlmod/other_module/other_module/pkg/BUILD.bazel
+++ b/examples/bzlmod/other_module/other_module/pkg/BUILD.bazel
@@ -1,4 +1,7 @@
-load("@python_3_11//:defs.bzl", py_binary_311 = "py_binary")
+load(
+ "@python_3_11//:defs.bzl",
+ py_binary_311 = "py_binary",
+)
load("@rules_python//python:defs.bzl", "py_library")
py_library(
@@ -13,11 +16,16 @@ py_library(
# used only when you need to support multiple versions of Python
# in the same project.
py_binary_311(
- name = "lib_311",
- srcs = ["lib.py"],
+ name = "bin",
+ srcs = ["bin.py"],
data = ["data/data.txt"],
+ main = "bin.py",
visibility = ["//visibility:public"],
- deps = ["@rules_python//python/runfiles"],
+ deps = [
+ ":lib",
+ "@other_module_pip//absl_py",
+ "@rules_python//python/runfiles",
+ ],
)
exports_files(["data/data.txt"])
diff --git a/examples/bzlmod/other_module/other_module/pkg/bin.py b/examples/bzlmod/other_module/other_module/pkg/bin.py
new file mode 100644
index 000000000..3e28ca23e
--- /dev/null
+++ b/examples/bzlmod/other_module/other_module/pkg/bin.py
@@ -0,0 +1,6 @@
+import sys
+
+import absl
+
+print("Python version:", sys.version)
+print("Module 'absl':", absl)
diff --git a/examples/bzlmod/other_module/requirements.in b/examples/bzlmod/other_module/requirements.in
new file mode 100644
index 000000000..b998a06a4
--- /dev/null
+++ b/examples/bzlmod/other_module/requirements.in
@@ -0,0 +1 @@
+absl-py
diff --git a/examples/bzlmod/other_module/requirements_lock_3_11.txt b/examples/bzlmod/other_module/requirements_lock_3_11.txt
new file mode 100644
index 000000000..7e350f278
--- /dev/null
+++ b/examples/bzlmod/other_module/requirements_lock_3_11.txt
@@ -0,0 +1,10 @@
+#
+# This file is autogenerated by pip-compile with Python 3.11
+# by the following command:
+#
+# bazel run //other_module/pkg:requirements.update
+#
+absl-py==1.4.0 \
+ --hash=sha256:0d3fe606adfa4f7db64792dd4c7aee4ee0c38ab75dfd353b7a83ed3e957fcb47 \
+ --hash=sha256:d2c244d01048ba476e7c080bd2c6df5e141d211de80223460d5b3b8a2a58433d
+ # via -r other_module/pkg/requirements.in
diff --git a/examples/bzlmod/tests/other_module/BUILD.bazel b/examples/bzlmod/tests/other_module/BUILD.bazel
new file mode 100644
index 000000000..1bd8a900a
--- /dev/null
+++ b/examples/bzlmod/tests/other_module/BUILD.bazel
@@ -0,0 +1,14 @@
+# Tests to verify the root module can interact with the "other_module"
+# submodule.
+#
+# Note that other_module is seen as "our_other_module" due to repo-remapping
+# in the root module.
+
+load("@bazel_skylib//rules:build_test.bzl", "build_test")
+
+build_test(
+ name = "other_module_bin_build_test",
+ targets = [
+ "@our_other_module//other_module/pkg:bin",
+ ],
+)
diff --git a/python/extensions/pip.bzl b/python/extensions/pip.bzl
index ca0b76584..90da4da7e 100644
--- a/python/extensions/pip.bzl
+++ b/python/extensions/pip.bzl
@@ -291,15 +291,10 @@ def _pip_impl(module_ctx):
for hub_name, whl_map in hub_whl_map.items():
for whl_name, version_map in whl_map.items():
- if DEFAULT_PYTHON_VERSION not in version_map:
- fail((
- "Default python version '{version}' missing in pip " +
- "hub '{hub}': update your pip.parse() calls so that " +
- 'includes `python_version = "{version}"`'
- ).format(
- version = DEFAULT_PYTHON_VERSION,
- hub = hub_name,
- ))
+ if DEFAULT_PYTHON_VERSION in version_map:
+ whl_default_version = DEFAULT_PYTHON_VERSION
+ else:
+ whl_default_version = None
# Create the alias repositories which contains different select
# statements These select statements point to the different pip
@@ -307,7 +302,7 @@ def _pip_impl(module_ctx):
whl_library_alias(
name = hub_name + "_" + whl_name,
wheel_name = whl_name,
- default_version = DEFAULT_PYTHON_VERSION,
+ default_version = whl_default_version,
version_map = version_map,
)
@@ -363,7 +358,7 @@ is used, this attribute defaults to that version of Python.
mandatory = False,
doc = """\
A dict of labels to wheel names that is typically generated by the whl_modifications.
-The labels are JSON config files describing the modifications.
+The labels are JSON config files describing the modifications.
""",
),
}, **pip_repository_attrs)
@@ -396,7 +391,7 @@ executable.""",
),
"copy_files": attr.string_dict(
doc = """\
-(dict, optional): A mapping of `src` and `out` files for
+(dict, optional): A mapping of `src` and `out` files for
[@bazel_skylib//rules:copy_file.bzl][cf]""",
),
"data": attr.string_list(
@@ -457,10 +452,10 @@ the BUILD files for wheels.
attrs = _pip_parse_ext_attrs(),
doc = """\
This tag class is used to create a pip hub and all of the spokes that are part of that hub.
-This tag class reuses most of the pip attributes that are found in
+This tag class reuses most of the pip attributes that are found in
@rules_python//python/pip_install:pip_repository.bzl.
-The exceptions are it does not use the args 'repo_prefix',
-and 'incompatible_generate_aliases'. We set the repository prefix
+The exceptions are it does not use the args 'repo_prefix',
+and 'incompatible_generate_aliases'. We set the repository prefix
for the user and the alias arg is always True in bzlmod.
""",
),
@@ -484,7 +479,7 @@ def _whl_mods_repo_impl(rctx):
_whl_mods_repo = repository_rule(
doc = """\
-This rule creates json files based on the whl_mods attribute.
+This rule creates json files based on the whl_mods attribute.
""",
implementation = _whl_mods_repo_impl,
attrs = {
diff --git a/python/pip.bzl b/python/pip.bzl
index cae15919b..708cd6ba6 100644
--- a/python/pip.bzl
+++ b/python/pip.bzl
@@ -22,6 +22,25 @@ load(":versions.bzl", "MINOR_MAPPING")
compile_pip_requirements = _compile_pip_requirements
package_annotation = _package_annotation
+_NO_MATCH_ERROR_MESSAGE_TEMPLATE = """\
+No matching wheel for current configuration's Python version.
+
+The current build configuration's Python version doesn't match any of the Python
+versions available for this wheel. This wheel supports the following Python versions:
+ {supported_versions}
+
+As matched by the `@{rules_python}//python/config_settings:is_python_`
+configuration settings.
+
+To determine the current configuration's Python version, run:
+ `bazel config ` (shown further below)
+and look for
+ {rules_python}//python/config_settings:python_version
+
+If the value is missing, then the "default" Python version is being used,
+which has a "null" version value and will not match version constraints.
+"""
+
def pip_install(requirements = None, name = "pip", **kwargs):
"""Accepts a locked/compiled requirements file and installs the dependencies listed within.
@@ -260,13 +279,10 @@ _multi_pip_parse = repository_rule(
def _whl_library_alias_impl(rctx):
rules_python = rctx.attr._rules_python_workspace.workspace_name
- if rctx.attr.default_version not in rctx.attr.version_map:
- fail(
- """
-Unable to find '{}' in your version map, you may need to update your requirement files.
- """.format(rctx.attr.version_map),
- )
- default_repo_prefix = rctx.attr.version_map[rctx.attr.default_version]
+ if rctx.attr.default_version:
+ default_repo_prefix = rctx.attr.version_map[rctx.attr.default_version]
+ else:
+ default_repo_prefix = None
version_map = rctx.attr.version_map.items()
build_content = ["# Generated by python/pip.bzl"]
for alias_name in ["pkg", "whl", "data", "dist_info"]:
@@ -289,6 +305,7 @@ def _whl_library_render_alias_target(
# is canonical, so we have to add a second @.
if BZLMOD_ENABLED:
rules_python = "@" + rules_python
+
alias = ["""\
alias(
name = "{alias_name}",
@@ -304,23 +321,42 @@ alias(
),
rules_python = rules_python,
))
- alias.append("""\
- "//conditions:default": "{default_actual}",
- }}),
- visibility = ["//visibility:public"],
-)""".format(
+ if default_repo_prefix:
default_actual = "@{repo_prefix}{wheel_name}//:{alias_name}".format(
repo_prefix = default_repo_prefix,
wheel_name = wheel_name,
alias_name = alias_name,
- ),
- ))
+ )
+ alias.append(' "//conditions:default": "{default_actual}",'.format(
+ default_actual = default_actual,
+ ))
+
+ alias.append(" },") # Close select expression condition dict
+ if not default_repo_prefix:
+ supported_versions = sorted([python_version for python_version, _ in version_map])
+ alias.append(' no_match_error="""{}""",'.format(
+ _NO_MATCH_ERROR_MESSAGE_TEMPLATE.format(
+ supported_versions = ", ".join(supported_versions),
+ rules_python = rules_python,
+ ),
+ ))
+ alias.append(" ),") # Close the select expression
+ alias.append(' visibility = ["//visibility:public"],')
+ alias.append(")") # Close the alias() expression
return "\n".join(alias)
whl_library_alias = repository_rule(
_whl_library_alias_impl,
attrs = {
- "default_version": attr.string(mandatory = True),
+ "default_version": attr.string(
+ mandatory = False,
+ doc = "Optional Python version in major.minor format, e.g. '3.10'." +
+ "The Python version of the wheel to use when the versions " +
+ "from `version_map` don't match. This allows the default " +
+ "(version unaware) rules to match and select a wheel. If " +
+ "not specified, then the default rules won't be able to " +
+ "resolve a wheel and an error will occur.",
+ ),
"version_map": attr.string_dict(mandatory = True),
"wheel_name": attr.string(mandatory = True),
"_rules_python_workspace": attr.label(default = Label("//:WORKSPACE")),