Skip to content

Commit a1f9e95

Browse files
committed
Allow protobuf 7
1 parent 5067ce5 commit a1f9e95

10 files changed

Lines changed: 50 additions & 32 deletions

File tree

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ jobs:
123123
- uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8
124124
- run: uv tool install poethepoet
125125
- run: uv remove google-adk --optional google-adk
126+
- run: uv add --dev --python 3.10 "googleapis-common-protos==1.70.0"
126127
- run: uv add --python 3.10 "protobuf<4"
127128
- run: uv sync --all-extras
128129
- run: poe build-develop

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ to docs, or any other relevant information.
1919

2020
## [Unreleased]
2121

22+
### Changed
23+
24+
- Relaxed the protobuf dependency bounds to allow protobuf 7 where compatible
25+
with the selected optional dependencies.
26+
2227
## [1.29.0] - 2026-06-17
2328

2429
### Added

pyproject.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ license-files = ["LICENSE"]
1010
keywords = ["temporal", "workflow"]
1111
dependencies = [
1212
"nexus-rpc==1.4.0",
13-
"protobuf>=3.20,<7.0.0",
13+
"protobuf>=3.20,<8.0.0",
1414
"python-dateutil>=2.8.2,<3 ; python_version < '3.11'",
15-
"types-protobuf>=3.20,<7.0.0",
15+
"types-protobuf>=3.20,<8.0.0",
1616
"typing-extensions>=4.2.0,<5",
1717
]
1818
classifiers = [
@@ -74,7 +74,7 @@ dev = [
7474
"openai-agents[litellm]>=0.14.0; python_version < '3.14'",
7575
"litellm>=1.83.0",
7676
"openinference-instrumentation-google-adk>=0.1.11",
77-
"googleapis-common-protos==1.70.0",
77+
"googleapis-common-protos>=1.75.0,<2",
7878
"pytest-rerunfailures>=16.1",
7979
"pytest-xdist>=3.6,<4",
8080
"moto[s3,server]>=5",

scripts/_proto/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ COPY ./ ./
99

1010
RUN mkdir -p ./temporalio/api
1111
RUN uv remove google-adk --optional google-adk
12+
RUN uv add --dev "googleapis-common-protos==1.70.0"
1213
RUN uv add "protobuf<4"
1314
RUN uv sync --all-extras
1415
RUN uv run scripts/gen_protos.py

scripts/gen_payload_visitor.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -290,17 +290,16 @@ def walk(self, desc: Descriptor) -> bool:
290290
continue
291291

292292
# Repeated fields (including maps which are represented as repeated messages)
293-
if field.label == FieldDescriptor.LABEL_REPEATED:
294-
if (
295-
field.message_type is not None
296-
and field.message_type.GetOptions().map_entry
297-
):
298-
val_fd = field.message_type.fields_by_name.get("value")
293+
if field.is_repeated:
294+
message_type = field.message_type
295+
if message_type is not None and message_type.GetOptions().map_entry:
296+
val_fd = message_type.fields_by_name.get("value")
299297
if (
300298
val_fd is not None
301299
and val_fd.type == FieldDescriptor.TYPE_MESSAGE
302300
):
303301
child_desc = val_fd.message_type
302+
assert child_desc is not None
304303
child_needed = self.walk(child_desc)
305304
if child_needed:
306305
has_payload = True
@@ -313,12 +312,13 @@ def walk(self, desc: Descriptor) -> bool:
313312
)
314313
)
315314

316-
key_fd = field.message_type.fields_by_name.get("key")
315+
key_fd = message_type.fields_by_name.get("key")
317316
if (
318317
key_fd is not None
319318
and key_fd.type == FieldDescriptor.TYPE_MESSAGE
320319
):
321320
child_desc = key_fd.message_type
321+
assert child_desc is not None
322322
child_needed = self.walk(child_desc)
323323
if child_needed:
324324
has_payload = True
@@ -331,14 +331,16 @@ def walk(self, desc: Descriptor) -> bool:
331331
)
332332
)
333333
else:
334+
assert message_type is not None
334335
item = self._collect_repeated(
335-
field.message_type, field, f"o.{field.name}"
336+
message_type, field, f"o.{field.name}"
336337
)
337338
if item is not None:
338339
has_payload = True
339340
emit_items.append(item)
340341
else:
341342
child_desc = field.message_type
343+
assert child_desc is not None
342344
child_has_payload = self.walk(child_desc)
343345
has_payload |= child_has_payload
344346
if child_has_payload:
@@ -358,6 +360,7 @@ def walk(self, desc: Descriptor) -> bool:
358360
first = True
359361
for field in fields:
360362
child_desc = field.message_type
363+
assert child_desc is not None
361364
child_has_payload = self.walk(child_desc)
362365
has_payload |= child_has_payload
363366
if child_has_payload:

temporalio/converter/_failure_converter.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,9 @@ def _nexus_failure_to_temporal_failure(
283283
failure.metadata
284284
and failure.metadata.get("type") == _TEMPORAL_FAILURE_PROTO_TYPE
285285
):
286-
google.protobuf.json_format.ParseDict(failure.details, temporal_failure)
286+
google.protobuf.json_format.ParseDict(
287+
dict(failure.details or {}), temporal_failure
288+
)
287289
else:
288290
temporal_failure.application_failure_info.SetInParent()
289291
temporal_failure.application_failure_info.type = "NexusFailure"

tests/conftest.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
f"Expected {temporalio.__file__} to be in {sys.prefix}"
3131
)
3232

33-
# Unless specifically overridden, we expect tests to run under protobuf 4.x/5.x lib
33+
# Unless specifically overridden, we expect tests to run under protobuf 4.x/5.x/6.x/7.x lib
3434
import google.protobuf
3535

3636
protobuf_version = google.protobuf.__version__
@@ -43,7 +43,8 @@
4343
protobuf_version.startswith("4.")
4444
or protobuf_version.startswith("5.")
4545
or protobuf_version.startswith("6.")
46-
), f"Expected protobuf 4.x/5.x/6.x, got {protobuf_version}"
46+
or protobuf_version.startswith("7.")
47+
), f"Expected protobuf 4.x/5.x/6.x/7.x, got {protobuf_version}"
4748

4849

4950
def pytest_runtest_setup(item): # type: ignore[reportMissingParameterType]

tests/nexus/test_temporal_system_nexus.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -147,12 +147,13 @@ def _build_proto_sample(message_type: type[Message]) -> Message:
147147

148148
def _populate_proto_sample(message: Message, *, path: str = "value") -> None:
149149
seen_oneofs: set[str] = set()
150-
for field in message.DESCRIPTOR.fields:
150+
for raw_field in message.DESCRIPTOR.fields:
151+
field = cast(FieldDescriptor, raw_field)
151152
if field.containing_oneof is not None:
152153
if field.containing_oneof.name in seen_oneofs:
153154
continue
154155
seen_oneofs.add(field.containing_oneof.name)
155-
if field.label == FieldDescriptor.LABEL_REPEATED:
156+
if field.is_repeated:
156157
if (
157158
field.message_type is not None
158159
and field.message_type.GetOptions().map_entry
@@ -186,8 +187,10 @@ def _populate_proto_map_entry(
186187
*,
187188
path: str,
188189
) -> None:
189-
key_field = field.message_type.fields_by_name["key"]
190-
value_field = field.message_type.fields_by_name["value"]
190+
message_type = field.message_type
191+
assert message_type is not None
192+
key_field = message_type.fields_by_name["key"]
193+
value_field = message_type.fields_by_name["value"]
191194
key = _proto_scalar_sample(key_field, path=f"{path}.{field.name}.key")
192195
container = getattr(message, field.name)
193196
if value_field.cpp_type == FieldDescriptor.CPPTYPE_MESSAGE:
@@ -222,10 +225,12 @@ def _proto_scalar_sample(field: FieldDescriptor, *, path: str) -> Any:
222225
):
223226
return 1.5
224227
if field.cpp_type == FieldDescriptor.CPPTYPE_ENUM:
225-
for enum_value in field.enum_type.values:
228+
enum_type = field.enum_type
229+
assert enum_type is not None
230+
for enum_value in enum_type.values:
226231
if enum_value.number != 0:
227232
return enum_value.number
228-
return field.enum_type.values[0].number
233+
return enum_type.values[0].number
229234
raise TypeError(f"Unhandled proto scalar sample at {path}: {field!r}")
230235

231236

tests/worker/test_command_aware_visitor.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,11 @@ def _get_workflow_command_protos_with_seq() -> Iterator[type[Any]]:
7878
"""Get concrete classes of all workflow command protos with a seq field."""
7979
for descriptor in workflow_commands_pb2.DESCRIPTOR.message_types_by_name.values():
8080
if "seq" in descriptor.fields_by_name:
81-
yield descriptor._concrete_class
81+
yield getattr(descriptor, "_concrete_class")
8282

8383

8484
def _get_workflow_activation_job_protos_with_seq() -> Iterator[type[Any]]:
8585
"""Get concrete classes of all workflow activation job protos with a seq field."""
8686
for descriptor in workflow_activation_pb2.DESCRIPTOR.message_types_by_name.values():
8787
if "seq" in descriptor.fields_by_name:
88-
yield descriptor._concrete_class
88+
yield getattr(descriptor, "_concrete_class")

uv.lock

Lines changed: 10 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)