feat: Remove and redirect py_proto_library to protobuf (#2604)
Protobuf team is taking ownership of `py_proto_library` and the
implementation was moved to protobuf repository.
Remove py_proto_library from rules_python, to prevent divergent
implementations.
Make a redirect with a deprecation warning, so that this doesn't break
any users.
Previously this was attempted in:
https://github.com/bazelbuild/rules_python/commit/d0e25cfb41446e481da6e85f04ad0ac5bcf7ea80
Work towards https://github.com/bazelbuild/rules_python/issues/2173,
https://github.com/bazelbuild/rules_python/issues/2543
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 61000a1..7255e9f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -52,6 +52,9 @@
{#v0-0-0-changed}
### Changed
+* (rules) `py_proto_library` is deprecated in favour of the
+ implementation in https://github.com/protocolbuffers/protobuf. It will be
+ removed in the future release.
* (pypi) {obj}`pip.override` will now be ignored instead of raising an error,
fixes [#2550](https://github.com/bazelbuild/rules_python/issues/2550).
* (rules) deprecation warnings for deprecated symbols have been turned off by
diff --git a/MODULE.bazel b/MODULE.bazel
index 89f1cd7..76710e4 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -10,7 +10,7 @@
bazel_dep(name = "platforms", version = "0.0.4")
# Those are loaded only when using py_proto_library
-bazel_dep(name = "rules_proto", version = "7.0.2")
+# Use py_proto_library directly from protobuf repository
bazel_dep(name = "protobuf", version = "29.0-rc2", repo_name = "com_google_protobuf")
internal_deps = use_extension("//python/private:internal_deps.bzl", "internal_deps")
diff --git a/WORKSPACE b/WORKSPACE
index 902af58..b97411e 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -166,9 +166,3 @@
"https://files.pythonhosted.org/packages/50/67/3e966d99a07d60a21a21d7ec016e9e4c2642a86fea251ec68677daf71d4d/numpy-1.25.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
],
)
-
-# rules_proto expects //external:python_headers to point at the python headers.
-bind(
- name = "python_headers",
- actual = "//python/cc:current_py_cc_headers",
-)
diff --git a/examples/bzlmod/MODULE.bazel b/examples/bzlmod/MODULE.bazel
index d8535a0..eaed078 100644
--- a/examples/bzlmod/MODULE.bazel
+++ b/examples/bzlmod/MODULE.bazel
@@ -12,9 +12,6 @@
path = "../..",
)
-# (py_proto_library specific) We are using rules_proto to define rules_proto targets to be consumed by py_proto_library.
-bazel_dep(name = "rules_proto", version = "6.0.0-rc1")
-
# (py_proto_library specific) Add the protobuf library for well-known types (e.g. `Any`, `Timestamp`, etc)
bazel_dep(name = "protobuf", version = "27.0", repo_name = "com_google_protobuf")
diff --git a/examples/bzlmod/py_proto_library/BUILD.bazel b/examples/bzlmod/py_proto_library/BUILD.bazel
index 24436b4..175589f 100644
--- a/examples/bzlmod/py_proto_library/BUILD.bazel
+++ b/examples/bzlmod/py_proto_library/BUILD.bazel
@@ -20,11 +20,12 @@
# Regression test for https://github.com/bazelbuild/rules_python/issues/2515
#
-# This test failed before https://github.com/bazelbuild/rules_python/pull/2516
+# This test fails before protobuf 30.0 release
# when ran with --legacy_external_runfiles=False (default in Bazel 8.0.0).
native_test(
name = "external_import_test",
src = "@foo_external//:py_binary_with_proto",
+ tags = ["manual"], # TODO: reenable when com_google_protobuf is upgraded
# Incompatible with Windows: native_test wrapping a py_binary doesn't work
# on Windows.
target_compatible_with = select({
diff --git a/examples/bzlmod/py_proto_library/foo_external/BUILD.bazel b/examples/bzlmod/py_proto_library/foo_external/BUILD.bazel
index 3fa22e0..183a3c2 100644
--- a/examples/bzlmod/py_proto_library/foo_external/BUILD.bazel
+++ b/examples/bzlmod/py_proto_library/foo_external/BUILD.bazel
@@ -1,5 +1,5 @@
-load("@rules_proto//proto:defs.bzl", "proto_library")
-load("@rules_python//python:proto.bzl", "py_proto_library")
+load("@com_google_protobuf//bazel:proto_library.bzl", "proto_library")
+load("@com_google_protobuf//bazel:py_proto_library.bzl", "py_proto_library")
load("@rules_python//python:py_binary.bzl", "py_binary")
package(default_visibility = ["//visibility:public"])
diff --git a/examples/bzlmod/py_proto_library/foo_external/MODULE.bazel b/examples/bzlmod/py_proto_library/foo_external/MODULE.bazel
index 5063f9b..aca6f98 100644
--- a/examples/bzlmod/py_proto_library/foo_external/MODULE.bazel
+++ b/examples/bzlmod/py_proto_library/foo_external/MODULE.bazel
@@ -5,4 +5,3 @@
bazel_dep(name = "rules_python", version = "1.0.0")
bazel_dep(name = "protobuf", version = "28.2", repo_name = "com_google_protobuf")
-bazel_dep(name = "rules_proto", version = "7.0.2")
diff --git a/internal_dev_deps.bzl b/internal_dev_deps.bzl
index 0304fb1..cd33475 100644
--- a/internal_dev_deps.bzl
+++ b/internal_dev_deps.bzl
@@ -178,13 +178,6 @@
)
http_archive(
- name = "rules_proto",
- sha256 = "904a8097fae42a690c8e08d805210e40cccb069f5f9a0f6727cf4faa7bed2c9c",
- strip_prefix = "rules_proto-6.0.0-rc1",
- url = "https://github.com/bazelbuild/rules_proto/releases/download/6.0.0-rc1/rules_proto-6.0.0-rc1.tar.gz",
- )
-
- http_archive(
name = "com_google_protobuf",
sha256 = "23082dca1ca73a1e9c6cbe40097b41e81f71f3b4d6201e36c134acc30a1b3660",
url = "https://github.com/protocolbuffers/protobuf/releases/download/v29.0-rc2/protobuf-29.0-rc2.zip",
diff --git a/python/BUILD.bazel b/python/BUILD.bazel
index b747e2f..5c6c6a4 100644
--- a/python/BUILD.bazel
+++ b/python/BUILD.bazel
@@ -116,7 +116,7 @@
],
visibility = ["//visibility:public"],
deps = [
- "//python/private/proto:py_proto_library_bzl",
+ "@com_google_protobuf//bazel:py_proto_library_bzl",
],
)
diff --git a/python/private/BUILD.bazel b/python/private/BUILD.bazel
index 14f52c5..2928dab 100644
--- a/python/private/BUILD.bazel
+++ b/python/private/BUILD.bazel
@@ -31,7 +31,6 @@
name = "distribution",
srcs = glob(["**"]) + [
"//python/private/api:distribution",
- "//python/private/proto:distribution",
"//python/private/pypi:distribution",
"//python/private/whl_filegroup:distribution",
"//tools/build_defs/python/private:distribution",
diff --git a/python/private/proto/BUILD.bazel b/python/private/proto/BUILD.bazel
deleted file mode 100644
index dd53845..0000000
--- a/python/private/proto/BUILD.bazel
+++ /dev/null
@@ -1,48 +0,0 @@
-# Copyright 2022 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("@bazel_skylib//:bzl_library.bzl", "bzl_library")
-load("@com_google_protobuf//bazel/toolchains:proto_lang_toolchain.bzl", "proto_lang_toolchain")
-
-package(default_visibility = ["//visibility:private"])
-
-licenses(["notice"])
-
-filegroup(
- name = "distribution",
- srcs = glob(["**"]),
- visibility = ["//python/private:__pkg__"],
-)
-
-bzl_library(
- name = "py_proto_library_bzl",
- srcs = ["py_proto_library.bzl"],
- visibility = ["//python:__pkg__"],
- deps = [
- "//python:py_info_bzl",
- "@com_google_protobuf//bazel/common:proto_common_bzl",
- "@com_google_protobuf//bazel/common:proto_info_bzl",
- "@rules_proto//proto:defs",
- ],
-)
-
-proto_lang_toolchain(
- name = "python_toolchain",
- command_line = "--python_out=%s",
- progress_message = "Generating Python proto_library %{label}",
- runtime = "@com_google_protobuf//:protobuf_python",
- # NOTE: This isn't *actually* public. It's an implicit dependency of py_proto_library,
- # so must be public so user usages of the rule can reference it.
- visibility = ["//visibility:public"],
-)
diff --git a/python/private/proto/py_proto_library.bzl b/python/private/proto/py_proto_library.bzl
deleted file mode 100644
index 1e9df84..0000000
--- a/python/private/proto/py_proto_library.bzl
+++ /dev/null
@@ -1,244 +0,0 @@
-# Copyright 2022 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.
-
-"""The implementation of the `py_proto_library` rule and its aspect."""
-
-load("@com_google_protobuf//bazel/common:proto_common.bzl", "proto_common")
-load("@com_google_protobuf//bazel/common:proto_info.bzl", "ProtoInfo")
-load("//python:py_info.bzl", "PyInfo")
-load("//python/api:api.bzl", _py_common = "py_common")
-
-PY_PROTO_TOOLCHAIN = "@rules_python//python/proto:toolchain_type"
-
-_PyProtoInfo = provider(
- doc = "Encapsulates information needed by the Python proto rules.",
- fields = {
- "imports": """
- (depset[str]) The field forwarding PyInfo.imports coming from
- the proto language runtime dependency.""",
- "py_info": "PyInfo from proto runtime (or other deps) to propagate.",
- "runfiles_from_proto_deps": """
- (depset[File]) Files from the transitive closure implicit proto
- dependencies""",
- "transitive_sources": """(depset[File]) The Python sources.""",
- },
-)
-
-def _filter_provider(provider, *attrs):
- return [dep[provider] for attr in attrs for dep in attr if provider in dep]
-
-def _incompatible_toolchains_enabled():
- return getattr(proto_common, "INCOMPATIBLE_ENABLE_PROTO_TOOLCHAIN_RESOLUTION", False)
-
-def _py_proto_aspect_impl(target, ctx):
- """Generates and compiles Python code for a proto_library.
-
- The function runs protobuf compiler on the `proto_library` target generating
- a .py file for each .proto file.
-
- Args:
- target: (Target) A target providing `ProtoInfo`. Usually this means a
- `proto_library` target, but not always; you must expect to visit
- non-`proto_library` targets, too.
- ctx: (RuleContext) The rule context.
-
- Returns:
- ([_PyProtoInfo]) Providers collecting transitive information about
- generated files.
- """
- _proto_library = ctx.rule.attr
-
- # Check Proto file names
- for proto in target[ProtoInfo].direct_sources:
- if proto.is_source and "-" in proto.dirname:
- fail("Cannot generate Python code for a .proto whose path contains '-' ({}).".format(
- proto.path,
- ))
-
- if _incompatible_toolchains_enabled():
- toolchain = ctx.toolchains[PY_PROTO_TOOLCHAIN]
- if not toolchain:
- fail("No toolchains registered for '%s'." % PY_PROTO_TOOLCHAIN)
- proto_lang_toolchain_info = toolchain.proto
- else:
- proto_lang_toolchain_info = getattr(ctx.attr, "_aspect_proto_toolchain")[proto_common.ProtoLangToolchainInfo]
-
- py_common = _py_common.get(ctx)
- py_info = py_common.PyInfoBuilder().merge_target(
- proto_lang_toolchain_info.runtime,
- ).build()
-
- api_deps = [proto_lang_toolchain_info.runtime]
-
- generated_sources = []
- proto_info = target[ProtoInfo]
- proto_root = proto_info.proto_source_root
- if proto_info.direct_sources:
- # Generate py files
- generated_sources = proto_common.declare_generated_files(
- actions = ctx.actions,
- proto_info = proto_info,
- extension = "_pb2.py",
- name_mapper = lambda name: name.replace("-", "_").replace(".", "/"),
- )
-
- # Handles multiple repository and virtual import cases
- if proto_root.startswith(ctx.bin_dir.path):
- proto_root = proto_root[len(ctx.bin_dir.path) + 1:]
-
- plugin_output = ctx.bin_dir.path + "/" + proto_root
-
- # Import path within the runfiles tree
- if proto_root.startswith("external/"):
- proto_root = proto_root[len("external") + 1:]
- else:
- proto_root = ctx.workspace_name + "/" + proto_root
-
- proto_common.compile(
- actions = ctx.actions,
- proto_info = proto_info,
- proto_lang_toolchain_info = proto_lang_toolchain_info,
- generated_files = generated_sources,
- plugin_output = plugin_output,
- )
-
- # Generated sources == Python sources
- python_sources = generated_sources
-
- deps = _filter_provider(_PyProtoInfo, getattr(_proto_library, "deps", []))
- runfiles_from_proto_deps = depset(
- transitive = [dep[DefaultInfo].default_runfiles.files for dep in api_deps] +
- [dep.runfiles_from_proto_deps for dep in deps],
- )
- transitive_sources = depset(
- direct = python_sources,
- transitive = [dep.transitive_sources for dep in deps],
- )
-
- return [
- _PyProtoInfo(
- imports = depset(
- # Adding to PYTHONPATH so the generated modules can be
- # imported. This is necessary when there is
- # strip_import_prefix, the Python modules are generated under
- # _virtual_imports. But it's undesirable otherwise, because it
- # will put the repo root at the top of the PYTHONPATH, ahead of
- # directories added through `imports` attributes.
- [proto_root] if "_virtual_imports" in proto_root else [],
- transitive = [dep[PyInfo].imports for dep in api_deps] + [dep.imports for dep in deps],
- ),
- runfiles_from_proto_deps = runfiles_from_proto_deps,
- transitive_sources = transitive_sources,
- py_info = py_info,
- ),
- ]
-
-_py_proto_aspect = aspect(
- implementation = _py_proto_aspect_impl,
- attrs = _py_common.API_ATTRS | (
- {} if _incompatible_toolchains_enabled() else {
- "_aspect_proto_toolchain": attr.label(
- default = ":python_toolchain",
- ),
- }
- ),
- attr_aspects = ["deps"],
- required_providers = [ProtoInfo],
- provides = [_PyProtoInfo],
- toolchains = [PY_PROTO_TOOLCHAIN] if _incompatible_toolchains_enabled() else [],
-)
-
-def _py_proto_library_rule(ctx):
- """Merges results of `py_proto_aspect` in `deps`.
-
- Args:
- ctx: (RuleContext) The rule context.
- Returns:
- ([PyInfo, DefaultInfo, OutputGroupInfo])
- """
- if not ctx.attr.deps:
- fail("'deps' attribute mustn't be empty.")
-
- pyproto_infos = _filter_provider(_PyProtoInfo, ctx.attr.deps)
- default_outputs = depset(
- transitive = [info.transitive_sources for info in pyproto_infos],
- )
-
- py_common = _py_common.get(ctx)
-
- py_info = py_common.PyInfoBuilder()
- py_info.set_has_py2_only_sources(False)
- py_info.set_has_py3_only_sources(False)
- py_info.transitive_sources.add(default_outputs)
- py_info.imports.add([info.imports for info in pyproto_infos])
- py_info.merge_all([
- pyproto_info.py_info
- for pyproto_info in pyproto_infos
- ])
- return [
- DefaultInfo(
- files = default_outputs,
- default_runfiles = ctx.runfiles(transitive_files = depset(
- transitive =
- [default_outputs] +
- [info.runfiles_from_proto_deps for info in pyproto_infos],
- )),
- ),
- OutputGroupInfo(
- default = depset(),
- ),
- py_info.build(),
- ]
-
-py_proto_library = rule(
- implementation = _py_proto_library_rule,
- doc = """
- Use `py_proto_library` to generate Python libraries from `.proto` files.
-
- The convention is to name the `py_proto_library` rule `foo_py_pb2`,
- when it is wrapping `proto_library` rule `foo_proto`.
-
- `deps` must point to a `proto_library` rule.
-
- Example:
-
-```starlark
-py_library(
- name = "lib",
- deps = [":foo_py_pb2"],
-)
-
-py_proto_library(
- name = "foo_py_pb2",
- deps = [":foo_proto"],
-)
-
-proto_library(
- name = "foo_proto",
- srcs = ["foo.proto"],
-)
-```""",
- attrs = {
- "deps": attr.label_list(
- doc = """
- The list of `proto_library` rules to generate Python libraries for.
-
- Usually this is just the one target: the proto library of interest.
- It can be any target providing `ProtoInfo`.""",
- providers = [ProtoInfo],
- aspects = [_py_proto_aspect],
- ),
- } | _py_common.API_ATTRS,
- provides = [PyInfo],
-)
diff --git a/python/proto.bzl b/python/proto.bzl
index 3f455ae..2ea9bdb 100644
--- a/python/proto.bzl
+++ b/python/proto.bzl
@@ -11,11 +11,11 @@
# 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.
-
"""
Python proto library.
"""
-load("//python/private/proto:py_proto_library.bzl", _py_proto_library = "py_proto_library")
+load("@com_google_protobuf//bazel:py_proto_library.bzl", _py_proto_library = "py_proto_library")
-py_proto_library = _py_proto_library
+def py_proto_library(*, deprecation = "Use py_proto_library from protobuf repository", **kwargs):
+ _py_proto_library(deprecation = deprecation, **kwargs)