From 583d60a3234eacaa3d3287d620e63e1cbcc8530f Mon Sep 17 00:00:00 2001 From: andy Date: Sat, 6 Jun 2026 09:00:03 +0800 Subject: [PATCH 1/2] fix: replace deprecated FieldDescriptor.label with is_repeated in proto_utils Use the non-deprecated FieldDescriptor.is_repeated property instead of comparing field.label to LABEL_REPEATED. Remove all TODO comments referencing issue #1011. Closes #1011 Co-Authored-By: Claude Opus 4.8 --- src/a2a/utils/proto_utils.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/a2a/utils/proto_utils.py b/src/a2a/utils/proto_utils.py index b191f98e0..e6f623ff7 100644 --- a/src/a2a/utils/proto_utils.py +++ b/src/a2a/utils/proto_utils.py @@ -174,10 +174,7 @@ def parse_params(params: QueryParams, message: ProtobufMessage) -> None: field = fields[k] v_list = params.getlist(k) - # TODO(https://github.com/a2aproject/a2a-python/issues/1011): Replace - # deprecated `field.label` with `field.is_repeated` once the minimum - # protobuf version requirement is bumped. - if field.label == FieldDescriptor.LABEL_REPEATED: + if field.is_repeated: accumulated: list[Any] = [] for v in v_list: if not v: @@ -211,10 +208,7 @@ def _check_required_field_violation( ) -> ValidationDetail | None: """Check if a required field is missing or invalid.""" val = getattr(msg, field.name) - # TODO(https://github.com/a2aproject/a2a-python/issues/1011): Replace - # deprecated `field.label` with `field.is_repeated` once the minimum - # protobuf version requirement is bumped. - if field.label == FieldDescriptor.LABEL_REPEATED: + if field.is_repeated: if not val: return ValidationDetail( field=field.name, @@ -255,10 +249,7 @@ def _recurse_validation( return errors val = getattr(msg, field.name) - # TODO(https://github.com/a2aproject/a2a-python/issues/1011): Replace - # deprecated `field.label` with `field.is_repeated` once the minimum - # protobuf version requirement is bumped. - if field.label != FieldDescriptor.LABEL_REPEATED: + if not field.is_repeated: if msg.HasField(field.name): sub_errs = _validate_proto_required_fields_internal(val) _append_nested_errors(errors, field.name, sub_errs) From 29dc4d32d3a724276b2911368f3edb85e878be9d Mon Sep 17 00:00:00 2001 From: andy Date: Sat, 6 Jun 2026 09:10:26 +0800 Subject: [PATCH 2/2] test: add test for repeated nested message validation in proto_utils Covers the _recurse_validation path for repeated message fields (e.g. Task.history), which was not previously tested. Co-Authored-By: Claude Opus 4.8 --- tests/utils/test_proto_utils.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/utils/test_proto_utils.py b/tests/utils/test_proto_utils.py index db49dbf05..d3b3a00ef 100644 --- a/tests/utils/test_proto_utils.py +++ b/tests/utils/test_proto_utils.py @@ -264,6 +264,25 @@ def test_missing_required_fields(self): assert {e['field'] for e in errors} == {'message_id', 'role', 'parts'} + def test_repeated_nested_message_validation(self): + """Test _recurse_validation for repeated message fields (Task.history).""" + task = Task( + id='task-1', + context_id='ctx-1', + status=TaskStatus(state=TaskState.TASK_STATE_WORKING), + history=[Message()], # empty message — missing required fields + ) + with pytest.raises(InvalidParamsError) as exc_info: + proto_utils.validate_proto_required_fields(task) + + err = exc_info.value + errors = err.data.get('errors', []) if err.data else [] + + fields = [e['field'] for e in errors] + assert 'history[0].message_id' in fields + assert 'history[0].role' in fields + assert 'history[0].parts' in fields + def test_nested_required_fields(self): """Test nested required fields inside TaskStatus.""" # Task Status requires 'state'