This is an automated email from the ASF dual-hosted git repository.
damccorm pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/beam.git
The following commit(s) were added to refs/heads/master by this push:
new bdc26675c91 Drop envoy-data-plane/betterproto dependency from Python
SDK (#39213)
bdc26675c91 is described below
commit bdc26675c91de1e7ca63f85d45b4c1fbdc97021f
Author: Shahar Epstein <[email protected]>
AuthorDate: Tue Jul 7 16:53:36 2026 +0300
Drop envoy-data-plane/betterproto dependency from Python SDK (#39213)
EnvoyRateLimiter only needed a handful of protobuf message classes from
envoy-data-plane, which pulls in the outdated betterproto==2.0.0b6 beta
(a protobuf reimplementation) plus grpclib and transitive deps. That pin
is the last blocker stopping Apache Airflow from un-suspending its Beam
provider (apache/airflow#66952), and it forced a Python-version split in
setup.py.
The dependency was already fought rather than used: the RateLimitServiceStub
bridge exists solely because betterproto emits async grpclib stubs that
don't
work with Beam's synchronous grpcio, and the wire is plain protobuf over
grpcio regardless.
Replace it with a minimal, self-contained rate_limit.proto compiled to a
checked-in rate_limit_pb2.py (following the existing
proto2_coder_test_messages_pb2.py precedent). Field numbers match Envoy's
rls.proto/ratelimit.proto, so it stays wire-compatible with a real RLS
server. This removes the conflict permanently for every downstream, deletes
the py<3.11 split, and drops betterproto + grpclib from every container
image.
New wire-format tests pin the field numbers/enum values so a renumbering
can't silently break live rate limiting (the mock-based tests would not).
Note: the container base_image_requirements.txt files had the two direct
packages removed to stay consistent with setup.py; a full
`generatePythonRequirementsAll` regeneration should follow in CI to also
prune now-orphaned transitives (grpclib, h2, multidict, ...) and refresh
pins.
Part of #37854
Unblocks apache/airflow#66952
Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
---
CHANGES.md | 1 +
sdks/python/.yapfignore | 1 +
.../apache_beam/io/components/rate_limit.proto | 64 ++++++++++++++++++++++
.../apache_beam/io/components/rate_limit_pb2.py | 63 +++++++++++++++++++++
.../apache_beam/io/components/rate_limiter.py | 39 ++++++-------
.../apache_beam/io/components/rate_limiter_test.py | 62 +++++++++++++++++----
.../container/ml/py310/base_image_requirements.txt | 2 -
.../container/ml/py310/gpu_image_requirements.txt | 2 -
.../container/ml/py311/base_image_requirements.txt | 2 -
.../container/ml/py311/gpu_image_requirements.txt | 2 -
.../container/ml/py312/base_image_requirements.txt | 2 -
.../container/ml/py312/gpu_image_requirements.txt | 2 -
.../container/ml/py313/base_image_requirements.txt | 2 -
.../container/py310/base_image_requirements.txt | 2 -
.../container/py311/base_image_requirements.txt | 2 -
.../container/py312/base_image_requirements.txt | 2 -
.../container/py313/base_image_requirements.txt | 2 -
.../container/py314/base_image_requirements.txt | 2 -
sdks/python/pyproject.toml | 1 +
sdks/python/scripts/run_lint.sh | 1 +
sdks/python/setup.py | 4 --
21 files changed, 198 insertions(+), 62 deletions(-)
diff --git a/CHANGES.md b/CHANGES.md
index 8bc74b958ba..ca3e0a4c425 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -72,6 +72,7 @@
## New Features / Improvements
+* (Python) Removed the `envoy-data-plane` (and transitive `betterproto`)
dependency; `EnvoyRateLimiter` now uses a small vendored protobuf definition
instead, resolving dependency conflicts for downstream projects
([#37854](https://github.com/apache/beam/issues/37854)).
* Dataflow Runner v2 has been renamed to Dataflow Portable Runner. Please
refer to Dataflow [public
documentation](https://docs.cloud.google.com/dataflow/docs/runner-v2) on when
to enable Portable
Runner.([#39000](https://github.com/apache/beam/issues/39000)).
* (Java) Enabled state tag encoding v2 by default for new Dataflow Streaming
Engine jobs. It can be disabled by passing
`--experiments=disable_streaming_engine_state_tag_encoding_v2` or
`--updateCompatibilityVersion=2.74.0` pipeline option. Note that the tag
encoding version cannot change during a job update. Jobs using tag encoding v2
(enabled by default for new jobs on 2.75.0+) cannot be downgraded to Beam
versions prior to 2.73.0, as only versions 2.73.0 and later support tag
encoding [...]
* (Python) Added instrumentation to support off-the-shelf profiling agents
when launching Python SDK Harness
([#38853](https://github.com/apache/beam/issues/38853)).
diff --git a/sdks/python/.yapfignore b/sdks/python/.yapfignore
index ca33e35e8fe..d37beaaac26 100644
--- a/sdks/python/.yapfignore
+++ b/sdks/python/.yapfignore
@@ -21,6 +21,7 @@
apache_beam/runners/dataflow/internal/clients/dataflow/dataflow_v1b3_messages.py
apache_beam/io/gcp/internal/clients/storage/storage_v1_client.py
apache_beam/io/gcp/internal/clients/storage/storage_v1_messages.py
apache_beam/coders/proto2_coder_test_messages_pb2.py
+apache_beam/io/components/rate_limit_pb2.py
apache_beam/portability/api/*
# Avoid excessive wrapping.
diff --git a/sdks/python/apache_beam/io/components/rate_limit.proto
b/sdks/python/apache_beam/io/components/rate_limit.proto
new file mode 100644
index 00000000000..dccf65f931a
--- /dev/null
+++ b/sdks/python/apache_beam/io/components/rate_limit.proto
@@ -0,0 +1,64 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You 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.
+
+// Minimal, self-contained subset of the Envoy Rate Limit Service protocol,
+// used by EnvoyRateLimiter in rate_limiter.py. Only the fields the client
+// touches are declared. Field numbers MUST match Envoy's rls.proto and
+// ratelimit.proto so this is wire-compatible with a real Envoy RLS server
+// (protobuf carries only field numbers and types on the wire, not message or
+// package names). This lets Beam use its standard protobuf/grpcio stack
+// instead of pulling in envoy-data-plane and its betterproto dependency.
+//
+// Regenerate rate_limit_pb2.py after editing this file:
+// cd sdks/python
+// python -m grpc_tools.protoc -I. --python_out=. \
+// apache_beam/io/components/rate_limit.proto
+// then prepend the Apache license header and remove the generated
+// `runtime_version` guard so the module stays compatible with the full
+// protobuf runtime range Beam supports (see setup.py).
+
+syntax = "proto3";
+
+package apache_beam.io.components.ratelimit;
+
+import "google/protobuf/duration.proto";
+
+message RateLimitDescriptor {
+ message Entry {
+ string key = 1;
+ string value = 2;
+ }
+ repeated Entry entries = 1;
+}
+
+message RateLimitRequest {
+ string domain = 1;
+ repeated RateLimitDescriptor descriptors = 2;
+ uint32 hits_addend = 3;
+}
+
+message RateLimitResponse {
+ enum Code {
+ UNKNOWN = 0;
+ OK = 1;
+ OVER_LIMIT = 2;
+ }
+ message DescriptorStatus {
+ Code code = 1;
+ google.protobuf.Duration duration_until_reset = 4;
+ }
+ Code overall_code = 1;
+ repeated DescriptorStatus statuses = 2;
+}
diff --git a/sdks/python/apache_beam/io/components/rate_limit_pb2.py
b/sdks/python/apache_beam/io/components/rate_limit_pb2.py
new file mode 100644
index 00000000000..1a7af827857
--- /dev/null
+++ b/sdks/python/apache_beam/io/components/rate_limit_pb2.py
@@ -0,0 +1,63 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+#
+
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler. DO NOT EDIT!
+# NO CHECKED-IN PROTOBUF GENCODE
+# source: apache_beam/io/components/rate_limit.proto
+#
+# Regenerate with (from sdks/python):
+# python -m grpc_tools.protoc -I. --python_out=. \
+# apache_beam/io/components/rate_limit.proto
+# then re-apply this license header and delete the generated
+# `runtime_version` guard so the module works across the full protobuf
+# runtime range Beam supports (see setup.py).
+
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import descriptor_pool as _descriptor_pool
+from google.protobuf import symbol_database as _symbol_database
+from google.protobuf.internal import builder as _builder
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+from google.protobuf import duration_pb2 as
google_dot_protobuf_dot_duration__pb2
+
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
+
b'\n*apache_beam/io/components/rate_limit.proto\x12#apache_beam.io.components.ratelimit\x1a\x1egoogle/protobuf/duration.proto\"\x8b\x01\n\x13RateLimitDescriptor\x12O\n\x07\x65ntries\x18\x01
\x03(\x0b\x32>.apache_beam.io.components.ratelimit.RateLimitDescriptor.Entry\x1a#\n\x05\x45ntry\x12\x0b\n\x03key\x18\x01
\x01(\t\x12\r\n\x05value\x18\x02
\x01(\t\"\x86\x01\n\x10RateLimitRequest\x12\x0e\n\x06\x64omain\x18\x01
\x01(\t\x12M\n\x0b\x64\x65scriptors\x18\x02 \x03(\x0b\x32\x38.apache_beam [...]
+)
+
+_globals = globals()
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
+_builder.BuildTopDescriptorsAndMessages(
+ DESCRIPTOR, 'apache_beam.io.components.rate_limit_pb2', _globals)
+if not _descriptor._USE_C_DESCRIPTORS:
+ DESCRIPTOR._loaded_options = None
+ _globals['_RATELIMITDESCRIPTOR']._serialized_start = 116
+ _globals['_RATELIMITDESCRIPTOR']._serialized_end = 255
+ _globals['_RATELIMITDESCRIPTOR_ENTRY']._serialized_start = 220
+ _globals['_RATELIMITDESCRIPTOR_ENTRY']._serialized_end = 255
+ _globals['_RATELIMITREQUEST']._serialized_start = 258
+ _globals['_RATELIMITREQUEST']._serialized_end = 392
+ _globals['_RATELIMITRESPONSE']._serialized_start = 395
+ _globals['_RATELIMITRESPONSE']._serialized_end = 786
+ _globals['_RATELIMITRESPONSE_DESCRIPTORSTATUS']._serialized_start = 591
+ _globals['_RATELIMITRESPONSE_DESCRIPTORSTATUS']._serialized_end = 741
+ _globals['_RATELIMITRESPONSE_CODE']._serialized_start = 743
+ _globals['_RATELIMITRESPONSE_CODE']._serialized_end = 786
+# @@protoc_insertion_point(module_scope)
diff --git a/sdks/python/apache_beam/io/components/rate_limiter.py
b/sdks/python/apache_beam/io/components/rate_limiter.py
index 4d6bf2f17a8..8e677973795 100644
--- a/sdks/python/apache_beam/io/components/rate_limiter.py
+++ b/sdks/python/apache_beam/io/components/rate_limiter.py
@@ -27,13 +27,9 @@ import threading
import time
import grpc
-from envoy_data_plane.envoy.extensions.common.ratelimit.v3 import
RateLimitDescriptor
-from envoy_data_plane.envoy.extensions.common.ratelimit.v3 import
RateLimitDescriptorEntry
-from envoy_data_plane.envoy.service.ratelimit.v3 import RateLimitRequest
-from envoy_data_plane.envoy.service.ratelimit.v3 import RateLimitResponse
-from envoy_data_plane.envoy.service.ratelimit.v3 import RateLimitResponseCode
from apache_beam.io.components import adaptive_throttler
+from apache_beam.io.components import rate_limit_pb2
from apache_beam.metrics import Metrics
_LOGGER = logging.getLogger(__name__)
@@ -122,19 +118,18 @@ class EnvoyRateLimiter(RateLimiter):
self._lock = threading.Lock()
class RateLimitServiceStub(object):
- """
- Wrapper for gRPC stub to be compatible with envoy_data_plane messages.
-
- The envoy-data-plane package uses 'betterproto' which generates async stubs
- for 'grpclib'. As Beam uses standard synchronous 'grpcio',
- RateLimitServiceStub is a bridge class to use the betterproto Message types
- (RateLimitRequest) with a standard grpcio Channel.
+ """
+ Minimal gRPC stub for the Envoy Rate Limit Service ShouldRateLimit method.
+
+ The method path is fixed by the Envoy RLS proto, so we bind it by hand
+ against a standard synchronous grpcio Channel using the protobuf message
+ types from rate_limit_pb2.
"""
def __init__(self, channel):
self.ShouldRateLimit = channel.unary_unary(
'/envoy.service.ratelimit.v3.RateLimitService/ShouldRateLimit',
- request_serializer=RateLimitRequest.SerializeToString,
- response_deserializer=RateLimitResponse.FromString,
+ request_serializer=rate_limit_pb2.RateLimitRequest.SerializeToString,
+ response_deserializer=rate_limit_pb2.RateLimitResponse.FromString,
)
def init_connection(self):
@@ -171,10 +166,11 @@ class EnvoyRateLimiter(RateLimiter):
for d in self.descriptors:
entries = []
for k, v in d.items():
- entries.append(RateLimitDescriptorEntry(key=k, value=v))
- proto_descriptors.append(RateLimitDescriptor(entries=entries))
+ entries.append(rate_limit_pb2.RateLimitDescriptor.Entry(key=k,
value=v))
+ proto_descriptors.append(
+ rate_limit_pb2.RateLimitDescriptor(entries=entries))
- request = RateLimitRequest(
+ request = rate_limit_pb2.RateLimitRequest(
domain=self.domain,
descriptors=proto_descriptors,
hits_addend=hits_added)
@@ -205,11 +201,11 @@ class EnvoyRateLimiter(RateLimiter):
e)
time.sleep(_RPC_RETRY_DELAY_SECONDS)
- if response.overall_code == RateLimitResponseCode.OK:
+ if response.overall_code == rate_limit_pb2.RateLimitResponse.OK:
self.requests_allowed.inc()
throttled = True
break
- elif response.overall_code == RateLimitResponseCode.OVER_LIMIT:
+ elif response.overall_code ==
rate_limit_pb2.RateLimitResponse.OVER_LIMIT:
self.requests_throttled.inc()
# Ratelimit exceeded, sleep for duration until reset and retry
# multiple rules can be set in the RLS config, so we need to find the
@@ -217,10 +213,9 @@ class EnvoyRateLimiter(RateLimiter):
sleep_s = 0.0
if response.statuses:
for status in response.statuses:
- if status.code == RateLimitResponseCode.OVER_LIMIT:
+ if status.code == rate_limit_pb2.RateLimitResponse.OVER_LIMIT:
dur = status.duration_until_reset
- # duration_until_reset is converted to timedelta by betterproto
- val = dur.total_seconds()
+ val = dur.ToTimedelta().total_seconds()
if val > sleep_s:
sleep_s = val
diff --git a/sdks/python/apache_beam/io/components/rate_limiter_test.py
b/sdks/python/apache_beam/io/components/rate_limiter_test.py
index 24d30a1c5c9..490d3a04e2d 100644
--- a/sdks/python/apache_beam/io/components/rate_limiter_test.py
+++ b/sdks/python/apache_beam/io/components/rate_limiter_test.py
@@ -16,16 +16,16 @@
#
import unittest
-from datetime import timedelta
from unittest import mock
import grpc
-from envoy_data_plane.envoy.service.ratelimit.v3 import RateLimitResponse
-from envoy_data_plane.envoy.service.ratelimit.v3 import RateLimitResponseCode
-from envoy_data_plane.envoy.service.ratelimit.v3 import
RateLimitResponseDescriptorStatus
+from google.protobuf.duration_pb2 import Duration
+from apache_beam.io.components import rate_limit_pb2
from apache_beam.io.components import rate_limiter
+RateLimitResponse = rate_limit_pb2.RateLimitResponse
+
class EnvoyRateLimiterTest(unittest.TestCase):
def setUp(self):
@@ -45,7 +45,7 @@ class EnvoyRateLimiterTest(unittest.TestCase):
def test_allow_success(self, mock_channel):
# Mock successful OK response
mock_stub = mock.Mock()
- mock_response = RateLimitResponse(overall_code=RateLimitResponseCode.OK)
+ mock_response = RateLimitResponse(overall_code=RateLimitResponse.OK)
mock_stub.ShouldRateLimit.return_value = mock_response
# Inject mock stub
@@ -60,8 +60,7 @@ class EnvoyRateLimiterTest(unittest.TestCase):
def test_allow_over_limit_retries_exceeded(self, mock_channel):
# Mock OVER_LIMIT response
mock_stub = mock.Mock()
- mock_response = RateLimitResponse(
- overall_code=RateLimitResponseCode.OVER_LIMIT)
+ mock_response =
RateLimitResponse(overall_code=RateLimitResponse.OVER_LIMIT)
mock_stub.ShouldRateLimit.return_value = mock_response
self.limiter._stub = mock_stub
@@ -86,7 +85,7 @@ class EnvoyRateLimiterTest(unittest.TestCase):
def test_allow_rpc_error_retry(self, mock_channel):
# Mock RpcError then Success
mock_stub = mock.Mock()
- mock_response = RateLimitResponse(overall_code=RateLimitResponseCode.OK)
+ mock_response = RateLimitResponse(overall_code=RateLimitResponse.OK)
# Side effect: Error, Error, Success
error = grpc.RpcError()
@@ -123,11 +122,11 @@ class EnvoyRateLimiterTest(unittest.TestCase):
mock_stub = mock.Mock()
# Valid until 5 seconds
- status = RateLimitResponseDescriptorStatus(
- code=RateLimitResponseCode.OVER_LIMIT,
- duration_until_reset=timedelta(seconds=5))
+ status = RateLimitResponse.DescriptorStatus(
+ code=RateLimitResponse.OVER_LIMIT,
+ duration_until_reset=Duration(seconds=5))
mock_response = RateLimitResponse(
- overall_code=RateLimitResponseCode.OVER_LIMIT, statuses=[status])
+ overall_code=RateLimitResponse.OVER_LIMIT, statuses=[status])
mock_stub.ShouldRateLimit.return_value = mock_response
self.limiter._stub = mock_stub
@@ -139,5 +138,44 @@ class EnvoyRateLimiterTest(unittest.TestCase):
mock_sleep.assert_called_with(5.0)
+class RateLimitWireFormatTest(unittest.TestCase):
+ """Pins the on-the-wire layout of the vendored rate_limit_pb2 messages.
+
+ Wire compatibility with a real Envoy Rate Limit Service depends solely on
+ field numbers and types (protobuf carries neither message nor package names
+ on the wire), so these must stay in lockstep with Envoy's rls.proto and
+ ratelimit.proto. The mock-based tests above would pass even if a field were
+ renumbered; these golden-byte assertions fail if that ever happens.
+ """
+ def test_request_wire_layout(self):
+ request = rate_limit_pb2.RateLimitRequest(
+ domain='d',
+ descriptors=[
+ rate_limit_pb2.RateLimitDescriptor(
+ entries=[
+ rate_limit_pb2.RateLimitDescriptor.Entry(
+ key='k', value='v')
+ ])
+ ],
+ hits_addend=1)
+ # domain=1 (LEN "d"); descriptors=2 (LEN {entries=1 (LEN {key=1 "k",
+ # value=2 "v"})}); hits_addend=3 (VARINT 1).
+ self.assertEqual(
+ request.SerializeToString().hex(), '0a016412080a060a016b1201761801')
+
+ def test_descriptor_status_wire_layout(self):
+ status = rate_limit_pb2.RateLimitResponse.DescriptorStatus(
+ code=rate_limit_pb2.RateLimitResponse.OVER_LIMIT,
+ duration_until_reset=Duration(seconds=5))
+ # code=1 (VARINT OVER_LIMIT=2); duration_until_reset=4 (LEN
+ # Duration{seconds=1 (VARINT 5)}).
+ self.assertEqual(status.SerializeToString().hex(), '080222020805')
+
+ def test_response_code_enum_values(self):
+ self.assertEqual(int(rate_limit_pb2.RateLimitResponse.UNKNOWN), 0)
+ self.assertEqual(int(rate_limit_pb2.RateLimitResponse.OK), 1)
+ self.assertEqual(int(rate_limit_pb2.RateLimitResponse.OVER_LIMIT), 2)
+
+
if __name__ == '__main__':
unittest.main()
diff --git a/sdks/python/container/ml/py310/base_image_requirements.txt
b/sdks/python/container/ml/py310/base_image_requirements.txt
index bd851ba7354..bec06ec1bef 100644
--- a/sdks/python/container/ml/py310/base_image_requirements.txt
+++ b/sdks/python/container/ml/py310/base_image_requirements.txt
@@ -35,7 +35,6 @@ attrs==26.1.0
backports.tarfile==1.2.0
beartype==0.22.9
beautifulsoup4==4.15.0
-betterproto==2.0.0b7
bs4==0.0.2
build==1.5.0
cachetools==6.2.6
@@ -52,7 +51,6 @@ distro==1.9.0
dnspython==2.8.0
docker==7.1.0
docstring_parser==0.18.0
-envoy-data-plane==0.2.6
exceptiongroup==1.3.1
execnet==2.1.2
fastavro==1.12.2
diff --git a/sdks/python/container/ml/py310/gpu_image_requirements.txt
b/sdks/python/container/ml/py310/gpu_image_requirements.txt
index a9846379404..4f9e02edf77 100644
--- a/sdks/python/container/ml/py310/gpu_image_requirements.txt
+++ b/sdks/python/container/ml/py310/gpu_image_requirements.txt
@@ -37,7 +37,6 @@ attrs==26.1.0
backports.tarfile==1.2.0
beartype==0.22.9
beautifulsoup4==4.15.0
-betterproto==2.0.0b7
blake3==1.0.9
bs4==0.0.2
build==1.5.0
@@ -67,7 +66,6 @@ docker==7.1.0
docstring_parser==0.18.0
einops==0.8.2
email-validator==2.3.0
-envoy-data-plane==0.2.6
exceptiongroup==1.3.1
execnet==2.1.2
fastapi==0.138.1
diff --git a/sdks/python/container/ml/py311/base_image_requirements.txt
b/sdks/python/container/ml/py311/base_image_requirements.txt
index 28278e54d34..3f9a18099fa 100644
--- a/sdks/python/container/ml/py311/base_image_requirements.txt
+++ b/sdks/python/container/ml/py311/base_image_requirements.txt
@@ -34,7 +34,6 @@ attrs==26.1.0
backports.tarfile==1.2.0
beartype==0.22.9
beautifulsoup4==4.15.0
-betterproto==2.0.0b6
bs4==0.0.2
build==1.5.0
cachetools==6.2.6
@@ -51,7 +50,6 @@ distro==1.9.0
dnspython==2.8.0
docker==7.1.0
docstring_parser==0.18.0
-envoy_data_plane==1.0.3
execnet==2.1.2
fastavro==1.12.2
fasteners==0.20
diff --git a/sdks/python/container/ml/py311/gpu_image_requirements.txt
b/sdks/python/container/ml/py311/gpu_image_requirements.txt
index 88fc7aad84c..7cb148c46f9 100644
--- a/sdks/python/container/ml/py311/gpu_image_requirements.txt
+++ b/sdks/python/container/ml/py311/gpu_image_requirements.txt
@@ -36,7 +36,6 @@ attrs==26.1.0
backports.tarfile==1.2.0
beartype==0.22.9
beautifulsoup4==4.15.0
-betterproto==2.0.0b6
blake3==1.0.9
bs4==0.0.2
build==1.5.0
@@ -66,7 +65,6 @@ docker==7.1.0
docstring_parser==0.18.0
einops==0.8.2
email-validator==2.3.0
-envoy_data_plane==1.0.3
execnet==2.1.2
fastapi==0.138.1
fastapi-cli==0.0.27
diff --git a/sdks/python/container/ml/py312/base_image_requirements.txt
b/sdks/python/container/ml/py312/base_image_requirements.txt
index 7e7c5728a4a..265696dc7e8 100644
--- a/sdks/python/container/ml/py312/base_image_requirements.txt
+++ b/sdks/python/container/ml/py312/base_image_requirements.txt
@@ -33,7 +33,6 @@ astunparse==1.6.3
attrs==26.1.0
beartype==0.22.9
beautifulsoup4==4.15.0
-betterproto==2.0.0b6
bs4==0.0.2
build==1.5.0
cachetools==6.2.6
@@ -50,7 +49,6 @@ distro==1.9.0
dnspython==2.8.0
docker==7.1.0
docstring_parser==0.18.0
-envoy_data_plane==1.0.3
execnet==2.1.2
fastavro==1.12.2
fasteners==0.20
diff --git a/sdks/python/container/ml/py312/gpu_image_requirements.txt
b/sdks/python/container/ml/py312/gpu_image_requirements.txt
index f563d4457dc..7cc83b10ca0 100644
--- a/sdks/python/container/ml/py312/gpu_image_requirements.txt
+++ b/sdks/python/container/ml/py312/gpu_image_requirements.txt
@@ -35,7 +35,6 @@ astunparse==1.6.3
attrs==26.1.0
beartype==0.22.9
beautifulsoup4==4.15.0
-betterproto==2.0.0b6
blake3==1.0.9
bs4==0.0.2
build==1.5.0
@@ -65,7 +64,6 @@ docker==7.1.0
docstring_parser==0.18.0
einops==0.8.2
email-validator==2.3.0
-envoy_data_plane==1.0.3
execnet==2.1.2
fastapi==0.138.1
fastapi-cli==0.0.27
diff --git a/sdks/python/container/ml/py313/base_image_requirements.txt
b/sdks/python/container/ml/py313/base_image_requirements.txt
index 390de543ed5..67f459b4fb2 100644
--- a/sdks/python/container/ml/py313/base_image_requirements.txt
+++ b/sdks/python/container/ml/py313/base_image_requirements.txt
@@ -33,7 +33,6 @@ astunparse==1.6.3
attrs==26.1.0
beartype==0.22.9
beautifulsoup4==4.15.0
-betterproto==2.0.0b6
bs4==0.0.2
build==1.5.0
cachetools==6.2.6
@@ -50,7 +49,6 @@ distro==1.9.0
dnspython==2.8.0
docker==7.1.0
docstring_parser==0.18.0
-envoy_data_plane==1.0.3
execnet==2.1.2
fastavro==1.12.2
fasteners==0.20
diff --git a/sdks/python/container/py310/base_image_requirements.txt
b/sdks/python/container/py310/base_image_requirements.txt
index 5c4bac1c2b2..85e7615cfdb 100644
--- a/sdks/python/container/py310/base_image_requirements.txt
+++ b/sdks/python/container/py310/base_image_requirements.txt
@@ -33,7 +33,6 @@ attrs==26.1.0
backports.tarfile==1.2.0
beartype==0.22.9
beautifulsoup4==4.15.0
-betterproto==2.0.0b7
bs4==0.0.2
build==1.5.0
cachetools==6.2.6
@@ -50,7 +49,6 @@ distro==1.9.0
dnspython==2.8.0
docker==7.1.0
docstring_parser==0.18.0
-envoy-data-plane==0.2.6
exceptiongroup==1.3.1
execnet==2.1.2
fastavro==1.12.2
diff --git a/sdks/python/container/py311/base_image_requirements.txt
b/sdks/python/container/py311/base_image_requirements.txt
index 3d9abe3dd9b..b9ede50e41d 100644
--- a/sdks/python/container/py311/base_image_requirements.txt
+++ b/sdks/python/container/py311/base_image_requirements.txt
@@ -32,7 +32,6 @@ attrs==26.1.0
backports.tarfile==1.2.0
beartype==0.22.9
beautifulsoup4==4.15.0
-betterproto==2.0.0b6
bs4==0.0.2
build==1.5.0
cachetools==6.2.6
@@ -49,7 +48,6 @@ distro==1.9.0
dnspython==2.8.0
docker==7.1.0
docstring_parser==0.18.0
-envoy_data_plane==1.0.3
execnet==2.1.2
fastavro==1.12.2
fasteners==0.20
diff --git a/sdks/python/container/py312/base_image_requirements.txt
b/sdks/python/container/py312/base_image_requirements.txt
index d6324c02562..4edcb642110 100644
--- a/sdks/python/container/py312/base_image_requirements.txt
+++ b/sdks/python/container/py312/base_image_requirements.txt
@@ -31,7 +31,6 @@ asn1crypto==1.5.1
attrs==26.1.0
beartype==0.22.9
beautifulsoup4==4.15.0
-betterproto==2.0.0b6
bs4==0.0.2
build==1.5.0
cachetools==6.2.6
@@ -48,7 +47,6 @@ distro==1.9.0
dnspython==2.8.0
docker==7.1.0
docstring_parser==0.18.0
-envoy_data_plane==1.0.3
execnet==2.1.2
fastavro==1.12.2
fasteners==0.20
diff --git a/sdks/python/container/py313/base_image_requirements.txt
b/sdks/python/container/py313/base_image_requirements.txt
index 0cdce630cad..a9728cd5e10 100644
--- a/sdks/python/container/py313/base_image_requirements.txt
+++ b/sdks/python/container/py313/base_image_requirements.txt
@@ -31,7 +31,6 @@ asn1crypto==1.5.1
attrs==26.1.0
beartype==0.22.9
beautifulsoup4==4.15.0
-betterproto==2.0.0b6
bs4==0.0.2
build==1.5.0
cachetools==6.2.6
@@ -48,7 +47,6 @@ distro==1.9.0
dnspython==2.8.0
docker==7.1.0
docstring_parser==0.18.0
-envoy_data_plane==1.0.3
execnet==2.1.2
fastavro==1.12.2
fasteners==0.20
diff --git a/sdks/python/container/py314/base_image_requirements.txt
b/sdks/python/container/py314/base_image_requirements.txt
index 66a95d3e331..1598d940e93 100644
--- a/sdks/python/container/py314/base_image_requirements.txt
+++ b/sdks/python/container/py314/base_image_requirements.txt
@@ -31,7 +31,6 @@ asn1crypto==1.5.1
attrs==26.1.0
beartype==0.22.9
beautifulsoup4==4.15.0
-betterproto==2.0.0b6
bs4==0.0.2
build==1.5.0
cachetools==6.2.6
@@ -48,7 +47,6 @@ distro==1.9.0
dnspython==2.8.0
docker==7.1.0
docstring_parser==0.18.0
-envoy_data_plane==1.0.3
execnet==2.1.2
fastavro==1.12.2
fasteners==0.20
diff --git a/sdks/python/pyproject.toml b/sdks/python/pyproject.toml
index 41c7d6d60d7..a3a59e878c2 100644
--- a/sdks/python/pyproject.toml
+++ b/sdks/python/pyproject.toml
@@ -82,6 +82,7 @@ skip = [
"storage_v1_client.py",
"storage_v1_messages.py",
"proto2_coder_test_messages_pb2.py",
+ "rate_limit_pb2.py",
"cloudbuild_v1_client.py",
"cloudbuild_v1_messages.py",
"boto3_client.py",
diff --git a/sdks/python/scripts/run_lint.sh b/sdks/python/scripts/run_lint.sh
index 58d75cd7bac..068c838952d 100755
--- a/sdks/python/scripts/run_lint.sh
+++ b/sdks/python/scripts/run_lint.sh
@@ -57,6 +57,7 @@ EXCLUDED_GENERATED_FILES=(
"apache_beam/io/gcp/internal/clients/storage/storage_v1_client.py"
"apache_beam/io/gcp/internal/clients/storage/storage_v1_messages.py"
"apache_beam/coders/proto2_coder_test_messages_pb2.py"
+"apache_beam/io/components/rate_limit_pb2.py"
"apache_beam/runners/dataflow/internal/clients/cloudbuild/cloudbuild_v1_client.py"
"apache_beam/runners/dataflow/internal/clients/cloudbuild/cloudbuild_v1_messages.py"
"apache_beam/io/aws/clients/s3/boto3_client.py"
diff --git a/sdks/python/setup.py b/sdks/python/setup.py
index 9a566b15d1d..e764f935bea 100644
--- a/sdks/python/setup.py
+++ b/sdks/python/setup.py
@@ -418,10 +418,6 @@ if __name__ == '__main__':
ext_modules=extensions,
install_requires=[
'cryptography>=39.0.0,<49.0.0',
- 'envoy-data-plane>=1.0.3,<2; python_version >= "3.11"',
- # Newer version only work on Python 3.11. Versions 0.3 <= ver < 1.x
- # conflict with other GCP dependencies.
- 'envoy-data-plane<0.3.0; python_version < "3.11"',
'fastavro>=0.23.6,<2',
'fasteners>=0.3,<1.0',
'grpcio>=1.33.1,<2,!=1.48.0,!=1.59.*,!=1.60.*,!=1.61.*,!=1.62.0,!=1.62.1,!=1.66.*,!=1.67.*,!=1.68.*,!=1.69.*,!=1.70.*',
# pylint: disable=line-too-long