refactor: toolchainize py_proto_library (#1577)

Enables use of `--incompatible_enable_proto_toolchain_resolution` flag
that launched in Bazel 7. This allows users to choose a pre-built
`protoc` or use the runtime from https://pypi.org/project/protobuf/
rather than be forced to use hard-coded values in Bazel core.

This change is also happening in other language rulesets that provide
first-class protobuf support, e.g.
https://github.com/bazelbuild/rules_go/issues/3895

No update to CHANGELOG.md in this PR as the feature is not yet
documented for end-users, this just makes it possible to enable the
flag. A follow-up PR will provide user instructions.

---------

Co-authored-by: Alex Eagle <alex@aspect.dev>
diff --git a/MODULE.bazel b/MODULE.bazel
index fc32a3e..c7d5e23 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -9,7 +9,7 @@
 bazel_dep(name = "platforms", version = "0.0.4")
 
 # Those are loaded only when using py_proto_library
-bazel_dep(name = "rules_proto", version = "5.3.0-21.7")
+bazel_dep(name = "rules_proto", version = "6.0.0-rc1")
 bazel_dep(name = "protobuf", version = "21.7", repo_name = "com_google_protobuf")
 
 internal_deps = use_extension("//python/private/bzlmod:internal_deps.bzl", "internal_deps")
diff --git a/examples/py_proto_library/WORKSPACE b/examples/py_proto_library/WORKSPACE
index bf38112..81f189d 100644
--- a/examples/py_proto_library/WORKSPACE
+++ b/examples/py_proto_library/WORKSPACE
@@ -1,4 +1,6 @@
-workspace(name = "rules_python_py_proto_library_example")
+# NB: short workspace name is required to workaround PATH length limitation, see
+# https://github.com/bazelbuild/bazel/issues/18683#issuecomment-1843857373
+workspace(name = "p")
 
 # The following local_path_override is only needed to run this example as part of our CI.
 local_repository(
@@ -24,21 +26,9 @@
 
 http_archive(
     name = "rules_proto",
-    sha256 = "dc3fb206a2cb3441b485eb1e423165b231235a1ea9b031b4433cf7bc1fa460dd",
-    strip_prefix = "rules_proto-5.3.0-21.7",
-    urls = [
-        "https://github.com/bazelbuild/rules_proto/archive/refs/tags/5.3.0-21.7.tar.gz",
-    ],
-)
-
-http_archive(
-    name = "com_google_protobuf",
-    sha256 = "75be42bd736f4df6d702a0e4e4d30de9ee40eac024c4b845d17ae4cc831fe4ae",
-    strip_prefix = "protobuf-21.7",
-    urls = [
-        "https://mirror.bazel.build/github.com/protocolbuffers/protobuf/archive/v21.7.tar.gz",
-        "https://github.com/protocolbuffers/protobuf/archive/v21.7.tar.gz",
-    ],
+    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",
 )
 
 load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains")
@@ -46,3 +36,14 @@
 rules_proto_dependencies()
 
 rules_proto_toolchains()
+
+http_archive(
+    name = "com_google_protobuf",
+    sha256 = "4fc5ff1b2c339fb86cd3a25f0b5311478ab081e65ad258c6789359cd84d421f8",
+    strip_prefix = "protobuf-26.1",
+    urls = ["https://github.com/protocolbuffers/protobuf/archive/v26.1.tar.gz"],
+)
+
+load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps")
+
+protobuf_deps()
diff --git a/internal_deps.bzl b/internal_deps.bzl
index 2ef0dc5..a8bfd47 100644
--- a/internal_deps.bzl
+++ b/internal_deps.bzl
@@ -166,11 +166,9 @@
 
     http_archive(
         name = "rules_proto",
-        sha256 = "dc3fb206a2cb3441b485eb1e423165b231235a1ea9b031b4433cf7bc1fa460dd",
-        strip_prefix = "rules_proto-5.3.0-21.7",
-        urls = [
-            "https://github.com/bazelbuild/rules_proto/archive/refs/tags/5.3.0-21.7.tar.gz",
-        ],
+        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(
diff --git a/python/private/proto/py_proto_library.bzl b/python/private/proto/py_proto_library.bzl
index 91faa2d..e123ff8 100644
--- a/python/private/proto/py_proto_library.bzl
+++ b/python/private/proto/py_proto_library.bzl
@@ -17,7 +17,7 @@
 load("@rules_proto//proto:defs.bzl", "ProtoInfo", "proto_common")
 load("//python:defs.bzl", "PyInfo")
 
-ProtoLangToolchainInfo = proto_common.ProtoLangToolchainInfo
+PY_PROTO_TOOLCHAIN = "@rules_python//python/proto:toolchain_type"
 
 _PyProtoInfo = provider(
     doc = "Encapsulates information needed by the Python proto rules.",
@@ -35,6 +35,9 @@
 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.
 
@@ -51,7 +54,6 @@
       ([_PyProtoInfo]) Providers collecting transitive information about
       generated files.
     """
-
     _proto_library = ctx.rule.attr
 
     # Check Proto file names
@@ -61,7 +63,14 @@
                 proto.path,
             ))
 
-    proto_lang_toolchain_info = ctx.attr._aspect_proto_toolchain[ProtoLangToolchainInfo]
+    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]
+
     api_deps = [proto_lang_toolchain_info.runtime]
 
     generated_sources = []
@@ -123,7 +132,7 @@
 
 _py_proto_aspect = aspect(
     implementation = _py_proto_aspect_impl,
-    attrs = {
+    attrs = {} if _incompatible_toolchains_enabled() else {
         "_aspect_proto_toolchain": attr.label(
             default = ":python_toolchain",
         ),
@@ -131,6 +140,7 @@
     attr_aspects = ["deps"],
     required_providers = [ProtoInfo],
     provides = [_PyProtoInfo],
+    toolchains = [PY_PROTO_TOOLCHAIN] if _incompatible_toolchains_enabled() else [],
 )
 
 def _py_proto_library_rule(ctx):