From e8b4305b4350b79237cd3d1c5298eaf82cf751e4 Mon Sep 17 00:00:00 2001 From: Steve Ayers Date: Tue, 3 Jun 2025 13:11:05 -0400 Subject: [PATCH 1/5] Update to Protovalidate 0.12.0 --- Makefile | 2 +- buf.yaml | 4 +- .../cases/required_field_proto3_pb2.py | 22 ++++- .../cases/required_field_proto3_pb2.pyi | 32 ++++++ .../validate/conformance/cases/wkt_any_pb2.py | 38 +++++-- .../conformance/cases/wkt_any_pb2.pyi | 31 ++++++ .../conformance/cases/wkt_duration_pb2.py | 82 ++++++++++------ .../conformance/cases/wkt_duration_pb2.pyi | 31 ++++++ .../conformance/cases/wkt_timestamp_pb2.py | 98 +++++++++++-------- .../conformance/cases/wkt_timestamp_pb2.pyi | 31 ++++++ 10 files changed, 288 insertions(+), 83 deletions(-) diff --git a/Makefile b/Makefile index f880fd7c..af5be1b9 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ ADD_LICENSE_HEADER := $(BIN)/license-header \ --copyright-holder "Buf Technologies, Inc." \ --year-range "2023-2025" # This version should be kept in sync with the version in buf.yaml -PROTOVALIDATE_VERSION ?= v0.11.0 +PROTOVALIDATE_VERSION ?= v0.12.0 # Version of the cel-spec that this implementation is conformant with # This should be kept in sync with the version in format_test.py CEL_SPEC_VERSION ?= v0.24.0 diff --git a/buf.yaml b/buf.yaml index db835e5d..c113b9c3 100644 --- a/buf.yaml +++ b/buf.yaml @@ -2,8 +2,8 @@ version: v2 modules: - path: proto deps: - - buf.build/bufbuild/protovalidate:v0.11.0 - - buf.build/bufbuild/protovalidate-testing:v0.11.0 + - buf.build/bufbuild/protovalidate:v0.12.0 + - buf.build/bufbuild/protovalidate-testing:v0.12.0 lint: use: - STANDARD diff --git a/gen/buf/validate/conformance/cases/required_field_proto3_pb2.py b/gen/buf/validate/conformance/cases/required_field_proto3_pb2.py index f3003576..359ee4d6 100644 --- a/gen/buf/validate/conformance/cases/required_field_proto3_pb2.py +++ b/gen/buf/validate/conformance/cases/required_field_proto3_pb2.py @@ -39,7 +39,7 @@ from buf.validate import validate_pb2 as buf_dot_validate_dot_validate__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n:buf/validate/conformance/cases/required_field_proto3.proto\x12\x1e\x62uf.validate.conformance.cases\x1a\x1b\x62uf/validate/validate.proto\"0\n\x14RequiredProto3Scalar\x12\x18\n\x03val\x18\x01 \x01(\tB\x06\xbaH\x03\xc8\x01\x01R\x03val\"?\n RequiredProto3ScalarIgnoreAlways\x12\x1b\n\x03val\x18\x01 \x01(\tB\t\xbaH\x06\xc8\x01\x01\xd8\x01\x03R\x03val\"E\n\x1cRequiredProto3OptionalScalar\x12\x1d\n\x03val\x18\x01 \x01(\tB\x06\xbaH\x03\xc8\x01\x01H\x00R\x03val\x88\x01\x01\x42\x06\n\x04_val\"T\n(RequiredProto3OptionalScalarIgnoreAlways\x12 \n\x03val\x18\x01 \x01(\tB\t\xbaH\x06\xc8\x01\x01\xd8\x01\x03H\x00R\x03val\x88\x01\x01\x42\x06\n\x04_val\"\x85\x01\n\x15RequiredProto3Message\x12S\n\x03val\x18\x01 \x01(\x0b\x32\x39.buf.validate.conformance.cases.RequiredProto3Message.MsgB\x06\xbaH\x03\xc8\x01\x01R\x03val\x1a\x17\n\x03Msg\x12\x10\n\x03val\x18\x01 \x01(\tR\x03val\"\xa0\x01\n!RequiredProto3MessageIgnoreAlways\x12\x62\n\x03val\x18\x01 \x01(\x0b\x32\x45.buf.validate.conformance.cases.RequiredProto3MessageIgnoreAlways.MsgB\t\xbaH\x06\xc8\x01\x01\xd8\x01\x03R\x03val\x1a\x17\n\x03Msg\x12\x10\n\x03val\x18\x01 \x01(\tR\x03val\"D\n\x13RequiredProto3OneOf\x12\x16\n\x01\x61\x18\x01 \x01(\tB\x06\xbaH\x03\xc8\x01\x01H\x00R\x01\x61\x12\x0e\n\x01\x62\x18\x02 \x01(\tH\x00R\x01\x62\x42\x05\n\x03val\"S\n\x1fRequiredProto3OneOfIgnoreAlways\x12\x19\n\x01\x61\x18\x01 \x01(\tB\t\xbaH\x06\xc8\x01\x01\xd8\x01\x03H\x00R\x01\x61\x12\x0e\n\x01\x62\x18\x02 \x01(\tH\x00R\x01\x62\x42\x05\n\x03val\"2\n\x16RequiredProto3Repeated\x12\x18\n\x03val\x18\x01 \x03(\tB\x06\xbaH\x03\xc8\x01\x01R\x03val\"A\n\"RequiredProto3RepeatedIgnoreAlways\x12\x1b\n\x03val\x18\x01 \x03(\tB\t\xbaH\x06\xc8\x01\x01\xd8\x01\x03R\x03val\"\xa1\x01\n\x11RequiredProto3Map\x12T\n\x03val\x18\x01 \x03(\x0b\x32:.buf.validate.conformance.cases.RequiredProto3Map.ValEntryB\x06\xbaH\x03\xc8\x01\x01R\x03val\x1a\x36\n\x08ValEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\"\xbc\x01\n\x1dRequiredProto3MapIgnoreAlways\x12\x63\n\x03val\x18\x01 \x03(\x0b\x32\x46.buf.validate.conformance.cases.RequiredProto3MapIgnoreAlways.ValEntryB\t\xbaH\x06\xc8\x01\x01\xd8\x01\x03R\x03val\x1a\x36\n\x08ValEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\x42\xda\x01\n\"com.buf.validate.conformance.casesB\x18RequiredFieldProto3ProtoP\x01\xa2\x02\x04\x42VCC\xaa\x02\x1e\x42uf.Validate.Conformance.Cases\xca\x02\x1e\x42uf\\Validate\\Conformance\\Cases\xe2\x02*Buf\\Validate\\Conformance\\Cases\\GPBMetadata\xea\x02!Buf::Validate::Conformance::Casesb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n:buf/validate/conformance/cases/required_field_proto3.proto\x12\x1e\x62uf.validate.conformance.cases\x1a\x1b\x62uf/validate/validate.proto\"0\n\x14RequiredProto3Scalar\x12\x18\n\x03val\x18\x01 \x01(\tB\x06\xbaH\x03\xc8\x01\x01R\x03val\"?\n RequiredProto3ScalarIgnoreAlways\x12\x1b\n\x03val\x18\x01 \x01(\tB\t\xbaH\x06\xc8\x01\x01\xd8\x01\x03R\x03val\"E\n\x1cRequiredProto3OptionalScalar\x12\x1d\n\x03val\x18\x01 \x01(\tB\x06\xbaH\x03\xc8\x01\x01H\x00R\x03val\x88\x01\x01\x42\x06\n\x04_val\"T\n(RequiredProto3OptionalScalarIgnoreAlways\x12 \n\x03val\x18\x01 \x01(\tB\t\xbaH\x06\xc8\x01\x01\xd8\x01\x03H\x00R\x03val\x88\x01\x01\x42\x06\n\x04_val\"\x85\x01\n\x15RequiredProto3Message\x12S\n\x03val\x18\x01 \x01(\x0b\x32\x39.buf.validate.conformance.cases.RequiredProto3Message.MsgB\x06\xbaH\x03\xc8\x01\x01R\x03val\x1a\x17\n\x03Msg\x12\x10\n\x03val\x18\x01 \x01(\tR\x03val\"\xa0\x01\n!RequiredProto3MessageIgnoreAlways\x12\x62\n\x03val\x18\x01 \x01(\x0b\x32\x45.buf.validate.conformance.cases.RequiredProto3MessageIgnoreAlways.MsgB\t\xbaH\x06\xc8\x01\x01\xd8\x01\x03R\x03val\x1a\x17\n\x03Msg\x12\x10\n\x03val\x18\x01 \x01(\tR\x03val\"D\n\x13RequiredProto3OneOf\x12\x16\n\x01\x61\x18\x01 \x01(\tB\x06\xbaH\x03\xc8\x01\x01H\x00R\x01\x61\x12\x0e\n\x01\x62\x18\x02 \x01(\tH\x00R\x01\x62\x42\x05\n\x03val\"S\n\x1fRequiredProto3OneOfIgnoreAlways\x12\x19\n\x01\x61\x18\x01 \x01(\tB\t\xbaH\x06\xc8\x01\x01\xd8\x01\x03H\x00R\x01\x61\x12\x0e\n\x01\x62\x18\x02 \x01(\tH\x00R\x01\x62\x42\x05\n\x03val\"2\n\x16RequiredProto3Repeated\x12\x18\n\x03val\x18\x01 \x03(\tB\x06\xbaH\x03\xc8\x01\x01R\x03val\"A\n\"RequiredProto3RepeatedIgnoreAlways\x12\x1b\n\x03val\x18\x01 \x03(\tB\t\xbaH\x06\xc8\x01\x01\xd8\x01\x03R\x03val\"\xa1\x01\n\x11RequiredProto3Map\x12T\n\x03val\x18\x01 \x03(\x0b\x32:.buf.validate.conformance.cases.RequiredProto3Map.ValEntryB\x06\xbaH\x03\xc8\x01\x01R\x03val\x1a\x36\n\x08ValEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\"\xbc\x01\n\x1dRequiredProto3MapIgnoreAlways\x12\x63\n\x03val\x18\x01 \x03(\x0b\x32\x46.buf.validate.conformance.cases.RequiredProto3MapIgnoreAlways.ValEntryB\t\xbaH\x06\xc8\x01\x01\xd8\x01\x03R\x03val\x1a\x36\n\x08ValEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\"\xac\x01\n\x14RequiredProto3MapKey\x12\\\n\x03val\x18\x01 \x03(\x0b\x32=.buf.validate.conformance.cases.RequiredProto3MapKey.ValEntryB\x0b\xbaH\x08\x9a\x01\x05\"\x03\xc8\x01\x01R\x03val\x1a\x36\n\x08ValEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\"\xb0\x01\n\x16RequiredProto3MapValue\x12^\n\x03val\x18\x01 \x03(\x0b\x32?.buf.validate.conformance.cases.RequiredProto3MapValue.ValEntryB\x0b\xbaH\x08\x9a\x01\x05*\x03\xc8\x01\x01R\x03val\x1a\x36\n\x08ValEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\";\n\x1aRequiredProto3RepeatedItem\x12\x1d\n\x03val\x18\x01 \x03(\tB\x0b\xbaH\x08\x92\x01\x05\"\x03\xc8\x01\x01R\x03valB\xda\x01\n\"com.buf.validate.conformance.casesB\x18RequiredFieldProto3ProtoP\x01\xa2\x02\x04\x42VCC\xaa\x02\x1e\x42uf.Validate.Conformance.Cases\xca\x02\x1e\x42uf\\Validate\\Conformance\\Cases\xe2\x02*Buf\\Validate\\Conformance\\Cases\\GPBMetadata\xea\x02!Buf::Validate::Conformance::Casesb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -75,6 +75,16 @@ _globals['_REQUIREDPROTO3MAPIGNOREALWAYS_VALENTRY']._serialized_options = b'8\001' _globals['_REQUIREDPROTO3MAPIGNOREALWAYS'].fields_by_name['val']._loaded_options = None _globals['_REQUIREDPROTO3MAPIGNOREALWAYS'].fields_by_name['val']._serialized_options = b'\272H\006\310\001\001\330\001\003' + _globals['_REQUIREDPROTO3MAPKEY_VALENTRY']._loaded_options = None + _globals['_REQUIREDPROTO3MAPKEY_VALENTRY']._serialized_options = b'8\001' + _globals['_REQUIREDPROTO3MAPKEY'].fields_by_name['val']._loaded_options = None + _globals['_REQUIREDPROTO3MAPKEY'].fields_by_name['val']._serialized_options = b'\272H\010\232\001\005\"\003\310\001\001' + _globals['_REQUIREDPROTO3MAPVALUE_VALENTRY']._loaded_options = None + _globals['_REQUIREDPROTO3MAPVALUE_VALENTRY']._serialized_options = b'8\001' + _globals['_REQUIREDPROTO3MAPVALUE'].fields_by_name['val']._loaded_options = None + _globals['_REQUIREDPROTO3MAPVALUE'].fields_by_name['val']._serialized_options = b'\272H\010\232\001\005*\003\310\001\001' + _globals['_REQUIREDPROTO3REPEATEDITEM'].fields_by_name['val']._loaded_options = None + _globals['_REQUIREDPROTO3REPEATEDITEM'].fields_by_name['val']._serialized_options = b'\272H\010\222\001\005\"\003\310\001\001' _globals['_REQUIREDPROTO3SCALAR']._serialized_start=123 _globals['_REQUIREDPROTO3SCALAR']._serialized_end=171 _globals['_REQUIREDPROTO3SCALARIGNOREALWAYS']._serialized_start=173 @@ -107,4 +117,14 @@ _globals['_REQUIREDPROTO3MAPIGNOREALWAYS']._serialized_end=1321 _globals['_REQUIREDPROTO3MAPIGNOREALWAYS_VALENTRY']._serialized_start=1076 _globals['_REQUIREDPROTO3MAPIGNOREALWAYS_VALENTRY']._serialized_end=1130 + _globals['_REQUIREDPROTO3MAPKEY']._serialized_start=1324 + _globals['_REQUIREDPROTO3MAPKEY']._serialized_end=1496 + _globals['_REQUIREDPROTO3MAPKEY_VALENTRY']._serialized_start=1076 + _globals['_REQUIREDPROTO3MAPKEY_VALENTRY']._serialized_end=1130 + _globals['_REQUIREDPROTO3MAPVALUE']._serialized_start=1499 + _globals['_REQUIREDPROTO3MAPVALUE']._serialized_end=1675 + _globals['_REQUIREDPROTO3MAPVALUE_VALENTRY']._serialized_start=1076 + _globals['_REQUIREDPROTO3MAPVALUE_VALENTRY']._serialized_end=1130 + _globals['_REQUIREDPROTO3REPEATEDITEM']._serialized_start=1677 + _globals['_REQUIREDPROTO3REPEATEDITEM']._serialized_end=1736 # @@protoc_insertion_point(module_scope) diff --git a/gen/buf/validate/conformance/cases/required_field_proto3_pb2.pyi b/gen/buf/validate/conformance/cases/required_field_proto3_pb2.pyi index 6e053867..30ec08d2 100644 --- a/gen/buf/validate/conformance/cases/required_field_proto3_pb2.pyi +++ b/gen/buf/validate/conformance/cases/required_field_proto3_pb2.pyi @@ -120,3 +120,35 @@ class RequiredProto3MapIgnoreAlways(_message.Message): VAL_FIELD_NUMBER: _ClassVar[int] val: _containers.ScalarMap[str, str] def __init__(self, val: _Optional[_Mapping[str, str]] = ...) -> None: ... + +class RequiredProto3MapKey(_message.Message): + __slots__ = ("val",) + class ValEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + VAL_FIELD_NUMBER: _ClassVar[int] + val: _containers.ScalarMap[str, str] + def __init__(self, val: _Optional[_Mapping[str, str]] = ...) -> None: ... + +class RequiredProto3MapValue(_message.Message): + __slots__ = ("val",) + class ValEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + VAL_FIELD_NUMBER: _ClassVar[int] + val: _containers.ScalarMap[str, str] + def __init__(self, val: _Optional[_Mapping[str, str]] = ...) -> None: ... + +class RequiredProto3RepeatedItem(_message.Message): + __slots__ = ("val",) + VAL_FIELD_NUMBER: _ClassVar[int] + val: _containers.RepeatedScalarFieldContainer[str] + def __init__(self, val: _Optional[_Iterable[str]] = ...) -> None: ... diff --git a/gen/buf/validate/conformance/cases/wkt_any_pb2.py b/gen/buf/validate/conformance/cases/wkt_any_pb2.py index e07b2a40..ad55d7dd 100644 --- a/gen/buf/validate/conformance/cases/wkt_any_pb2.py +++ b/gen/buf/validate/conformance/cases/wkt_any_pb2.py @@ -38,9 +38,11 @@ from buf.validate import validate_pb2 as buf_dot_validate_dot_validate__pb2 from google.protobuf import any_pb2 as google_dot_protobuf_dot_any__pb2 +from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 +from google.protobuf import wrappers_pb2 as google_dot_protobuf_dot_wrappers__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n,buf/validate/conformance/cases/wkt_any.proto\x12\x1e\x62uf.validate.conformance.cases\x1a\x1b\x62uf/validate/validate.proto\x1a\x19google/protobuf/any.proto\"1\n\x07\x41nyNone\x12&\n\x03val\x18\x01 \x01(\x0b\x32\x14.google.protobuf.AnyR\x03val\"=\n\x0b\x41nyRequired\x12.\n\x03val\x18\x01 \x01(\x0b\x32\x14.google.protobuf.AnyB\x06\xbaH\x03\xc8\x01\x01R\x03val\"e\n\x05\x41nyIn\x12\\\n\x03val\x18\x01 \x01(\x0b\x32\x14.google.protobuf.AnyB4\xbaH1\xa2\x01.\x12,type.googleapis.com/google.protobuf.DurationR\x03val\"i\n\x08\x41nyNotIn\x12]\n\x03val\x18\x01 \x01(\x0b\x32\x14.google.protobuf.AnyB5\xbaH2\xa2\x01/\x1a-type.googleapis.com/google.protobuf.TimestampR\x03valB\xcd\x01\n\"com.buf.validate.conformance.casesB\x0bWktAnyProtoP\x01\xa2\x02\x04\x42VCC\xaa\x02\x1e\x42uf.Validate.Conformance.Cases\xca\x02\x1e\x42uf\\Validate\\Conformance\\Cases\xe2\x02*Buf\\Validate\\Conformance\\Cases\\GPBMetadata\xea\x02!Buf::Validate::Conformance::Casesb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n,buf/validate/conformance/cases/wkt_any.proto\x12\x1e\x62uf.validate.conformance.cases\x1a\x1b\x62uf/validate/validate.proto\x1a\x19google/protobuf/any.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1egoogle/protobuf/wrappers.proto\"1\n\x07\x41nyNone\x12&\n\x03val\x18\x01 \x01(\x0b\x32\x14.google.protobuf.AnyR\x03val\"=\n\x0b\x41nyRequired\x12.\n\x03val\x18\x01 \x01(\x0b\x32\x14.google.protobuf.AnyB\x06\xbaH\x03\xc8\x01\x01R\x03val\"e\n\x05\x41nyIn\x12\\\n\x03val\x18\x01 \x01(\x0b\x32\x14.google.protobuf.AnyB4\xbaH1\xa2\x01.\x12,type.googleapis.com/google.protobuf.DurationR\x03val\"i\n\x08\x41nyNotIn\x12]\n\x03val\x18\x01 \x01(\x0b\x32\x14.google.protobuf.AnyB5\xbaH2\xa2\x01/\x1a-type.googleapis.com/google.protobuf.TimestampR\x03val\"]\n\x12\x41nyWrongTypeScalar\x12G\n\x03val\x18\x01 \x01(\tB5\xbaH2\xa2\x01/\x1a-type.googleapis.com/google.protobuf.TimestampR\x03val\"\xbd\x01\n\x13\x41nyWrongTypeMessage\x12\x86\x01\n\x03val\x18\x01 \x01(\x0b\x32=.buf.validate.conformance.cases.AnyWrongTypeMessage.WrongTypeB5\xbaH2\xa2\x01/\x1a-type.googleapis.com/google.protobuf.TimestampR\x03val\x1a\x1d\n\tWrongType\x12\x10\n\x03val\x18\x01 \x01(\x05R\x03val\"{\n\x13\x41nyWrongTypeWrapper\x12\x64\n\x03val\x18\x01 \x01(\x0b\x32\x1b.google.protobuf.Int32ValueB5\xbaH2\xa2\x01/\x1a-type.googleapis.com/google.protobuf.TimestampR\x03val\"v\n\x0f\x41nyWrongTypeWKT\x12\x63\n\x03val\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.TimestampB5\xbaH2\xa2\x01/\x1a-type.googleapis.com/google.protobuf.TimestampR\x03valB\xcd\x01\n\"com.buf.validate.conformance.casesB\x0bWktAnyProtoP\x01\xa2\x02\x04\x42VCC\xaa\x02\x1e\x42uf.Validate.Conformance.Cases\xca\x02\x1e\x42uf\\Validate\\Conformance\\Cases\xe2\x02*Buf\\Validate\\Conformance\\Cases\\GPBMetadata\xea\x02!Buf::Validate::Conformance::Casesb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -54,12 +56,30 @@ _globals['_ANYIN'].fields_by_name['val']._serialized_options = b'\272H1\242\001.\022,type.googleapis.com/google.protobuf.Duration' _globals['_ANYNOTIN'].fields_by_name['val']._loaded_options = None _globals['_ANYNOTIN'].fields_by_name['val']._serialized_options = b'\272H2\242\001/\032-type.googleapis.com/google.protobuf.Timestamp' - _globals['_ANYNONE']._serialized_start=136 - _globals['_ANYNONE']._serialized_end=185 - _globals['_ANYREQUIRED']._serialized_start=187 - _globals['_ANYREQUIRED']._serialized_end=248 - _globals['_ANYIN']._serialized_start=250 - _globals['_ANYIN']._serialized_end=351 - _globals['_ANYNOTIN']._serialized_start=353 - _globals['_ANYNOTIN']._serialized_end=458 + _globals['_ANYWRONGTYPESCALAR'].fields_by_name['val']._loaded_options = None + _globals['_ANYWRONGTYPESCALAR'].fields_by_name['val']._serialized_options = b'\272H2\242\001/\032-type.googleapis.com/google.protobuf.Timestamp' + _globals['_ANYWRONGTYPEMESSAGE'].fields_by_name['val']._loaded_options = None + _globals['_ANYWRONGTYPEMESSAGE'].fields_by_name['val']._serialized_options = b'\272H2\242\001/\032-type.googleapis.com/google.protobuf.Timestamp' + _globals['_ANYWRONGTYPEWRAPPER'].fields_by_name['val']._loaded_options = None + _globals['_ANYWRONGTYPEWRAPPER'].fields_by_name['val']._serialized_options = b'\272H2\242\001/\032-type.googleapis.com/google.protobuf.Timestamp' + _globals['_ANYWRONGTYPEWKT'].fields_by_name['val']._loaded_options = None + _globals['_ANYWRONGTYPEWKT'].fields_by_name['val']._serialized_options = b'\272H2\242\001/\032-type.googleapis.com/google.protobuf.Timestamp' + _globals['_ANYNONE']._serialized_start=201 + _globals['_ANYNONE']._serialized_end=250 + _globals['_ANYREQUIRED']._serialized_start=252 + _globals['_ANYREQUIRED']._serialized_end=313 + _globals['_ANYIN']._serialized_start=315 + _globals['_ANYIN']._serialized_end=416 + _globals['_ANYNOTIN']._serialized_start=418 + _globals['_ANYNOTIN']._serialized_end=523 + _globals['_ANYWRONGTYPESCALAR']._serialized_start=525 + _globals['_ANYWRONGTYPESCALAR']._serialized_end=618 + _globals['_ANYWRONGTYPEMESSAGE']._serialized_start=621 + _globals['_ANYWRONGTYPEMESSAGE']._serialized_end=810 + _globals['_ANYWRONGTYPEMESSAGE_WRONGTYPE']._serialized_start=781 + _globals['_ANYWRONGTYPEMESSAGE_WRONGTYPE']._serialized_end=810 + _globals['_ANYWRONGTYPEWRAPPER']._serialized_start=812 + _globals['_ANYWRONGTYPEWRAPPER']._serialized_end=935 + _globals['_ANYWRONGTYPEWKT']._serialized_start=937 + _globals['_ANYWRONGTYPEWKT']._serialized_end=1055 # @@protoc_insertion_point(module_scope) diff --git a/gen/buf/validate/conformance/cases/wkt_any_pb2.pyi b/gen/buf/validate/conformance/cases/wkt_any_pb2.pyi index a047d05c..3be0fdfa 100644 --- a/gen/buf/validate/conformance/cases/wkt_any_pb2.pyi +++ b/gen/buf/validate/conformance/cases/wkt_any_pb2.pyi @@ -14,6 +14,8 @@ from buf.validate import validate_pb2 as _validate_pb2 from google.protobuf import any_pb2 as _any_pb2 +from google.protobuf import timestamp_pb2 as _timestamp_pb2 +from google.protobuf import wrappers_pb2 as _wrappers_pb2 from google.protobuf import descriptor as _descriptor from google.protobuf import message as _message from collections.abc import Mapping as _Mapping @@ -44,3 +46,32 @@ class AnyNotIn(_message.Message): VAL_FIELD_NUMBER: _ClassVar[int] val: _any_pb2.Any def __init__(self, val: _Optional[_Union[_any_pb2.Any, _Mapping]] = ...) -> None: ... + +class AnyWrongTypeScalar(_message.Message): + __slots__ = ("val",) + VAL_FIELD_NUMBER: _ClassVar[int] + val: str + def __init__(self, val: _Optional[str] = ...) -> None: ... + +class AnyWrongTypeMessage(_message.Message): + __slots__ = ("val",) + class WrongType(_message.Message): + __slots__ = ("val",) + VAL_FIELD_NUMBER: _ClassVar[int] + val: int + def __init__(self, val: _Optional[int] = ...) -> None: ... + VAL_FIELD_NUMBER: _ClassVar[int] + val: AnyWrongTypeMessage.WrongType + def __init__(self, val: _Optional[_Union[AnyWrongTypeMessage.WrongType, _Mapping]] = ...) -> None: ... + +class AnyWrongTypeWrapper(_message.Message): + __slots__ = ("val",) + VAL_FIELD_NUMBER: _ClassVar[int] + val: _wrappers_pb2.Int32Value + def __init__(self, val: _Optional[_Union[_wrappers_pb2.Int32Value, _Mapping]] = ...) -> None: ... + +class AnyWrongTypeWKT(_message.Message): + __slots__ = ("val",) + VAL_FIELD_NUMBER: _ClassVar[int] + val: _timestamp_pb2.Timestamp + def __init__(self, val: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ...) -> None: ... diff --git a/gen/buf/validate/conformance/cases/wkt_duration_pb2.py b/gen/buf/validate/conformance/cases/wkt_duration_pb2.py index 34928d3c..dc3b386a 100644 --- a/gen/buf/validate/conformance/cases/wkt_duration_pb2.py +++ b/gen/buf/validate/conformance/cases/wkt_duration_pb2.py @@ -38,9 +38,11 @@ from buf.validate import validate_pb2 as buf_dot_validate_dot_validate__pb2 from google.protobuf import duration_pb2 as google_dot_protobuf_dot_duration__pb2 +from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 +from google.protobuf import wrappers_pb2 as google_dot_protobuf_dot_wrappers__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n1buf/validate/conformance/cases/wkt_duration.proto\x12\x1e\x62uf.validate.conformance.cases\x1a\x1b\x62uf/validate/validate.proto\x1a\x1egoogle/protobuf/duration.proto\";\n\x0c\x44urationNone\x12+\n\x03val\x18\x01 \x01(\x0b\x32\x19.google.protobuf.DurationR\x03val\"G\n\x10\x44urationRequired\x12\x33\n\x03val\x18\x01 \x01(\x0b\x32\x19.google.protobuf.DurationB\x06\xbaH\x03\xc8\x01\x01R\x03val\"H\n\rDurationConst\x12\x37\n\x03val\x18\x01 \x01(\x0b\x32\x19.google.protobuf.DurationB\n\xbaH\x07\xaa\x01\x04\x12\x02\x08\x03R\x03val\"J\n\nDurationIn\x12<\n\x03val\x18\x01 \x01(\x0b\x32\x19.google.protobuf.DurationB\x0f\xbaH\x0c\xaa\x01\t:\x02\x08\x01:\x03\x10\xe8\x07R\x03val\"F\n\rDurationNotIn\x12\x35\n\x03val\x18\x01 \x01(\x0b\x32\x19.google.protobuf.DurationB\x08\xbaH\x05\xaa\x01\x02\x42\x00R\x03val\"C\n\nDurationLT\x12\x35\n\x03val\x18\x01 \x01(\x0b\x32\x19.google.protobuf.DurationB\x08\xbaH\x05\xaa\x01\x02\x1a\x00R\x03val\"F\n\x0b\x44urationLTE\x12\x37\n\x03val\x18\x01 \x01(\x0b\x32\x19.google.protobuf.DurationB\n\xbaH\x07\xaa\x01\x04\"\x02\x08\x01R\x03val\"F\n\nDurationGT\x12\x38\n\x03val\x18\x01 \x01(\x0b\x32\x19.google.protobuf.DurationB\x0b\xbaH\x08\xaa\x01\x05*\x03\x10\xe8\x07R\x03val\"H\n\x0b\x44urationGTE\x12\x39\n\x03val\x18\x01 \x01(\x0b\x32\x19.google.protobuf.DurationB\x0c\xbaH\t\xaa\x01\x06\x32\x04\x10\xc0\x84=R\x03val\"I\n\x0c\x44urationGTLT\x12\x39\n\x03val\x18\x01 \x01(\x0b\x32\x19.google.protobuf.DurationB\x0c\xbaH\t\xaa\x01\x06\x1a\x02\x08\x01*\x00R\x03val\"K\n\x0e\x44urationExLTGT\x12\x39\n\x03val\x18\x01 \x01(\x0b\x32\x19.google.protobuf.DurationB\x0c\xbaH\t\xaa\x01\x06\x1a\x00*\x02\x08\x01R\x03val\"N\n\x0e\x44urationGTELTE\x12<\n\x03val\x18\x01 \x01(\x0b\x32\x19.google.protobuf.DurationB\x0f\xbaH\x0c\xaa\x01\t\"\x03\x08\x90\x1c\x32\x02\x08 None: ... + +class DurationWrongTypeScalar(_message.Message): + __slots__ = ("seconds",) + SECONDS_FIELD_NUMBER: _ClassVar[int] + seconds: int + def __init__(self, seconds: _Optional[int] = ...) -> None: ... + +class DurationWrongTypeMessage(_message.Message): + __slots__ = ("val",) + class WrongType(_message.Message): + __slots__ = ("seconds",) + SECONDS_FIELD_NUMBER: _ClassVar[int] + seconds: int + def __init__(self, seconds: _Optional[int] = ...) -> None: ... + VAL_FIELD_NUMBER: _ClassVar[int] + val: DurationWrongTypeMessage.WrongType + def __init__(self, val: _Optional[_Union[DurationWrongTypeMessage.WrongType, _Mapping]] = ...) -> None: ... + +class DurationWrongTypeWrapper(_message.Message): + __slots__ = ("val",) + VAL_FIELD_NUMBER: _ClassVar[int] + val: _wrappers_pb2.Int32Value + def __init__(self, val: _Optional[_Union[_wrappers_pb2.Int32Value, _Mapping]] = ...) -> None: ... + +class DurationWrongTypeWKT(_message.Message): + __slots__ = ("val",) + VAL_FIELD_NUMBER: _ClassVar[int] + val: _timestamp_pb2.Timestamp + def __init__(self, val: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ...) -> None: ... diff --git a/gen/buf/validate/conformance/cases/wkt_timestamp_pb2.py b/gen/buf/validate/conformance/cases/wkt_timestamp_pb2.py index 105280ec..9d8fb32e 100644 --- a/gen/buf/validate/conformance/cases/wkt_timestamp_pb2.py +++ b/gen/buf/validate/conformance/cases/wkt_timestamp_pb2.py @@ -37,10 +37,12 @@ from buf.validate import validate_pb2 as buf_dot_validate_dot_validate__pb2 +from google.protobuf import duration_pb2 as google_dot_protobuf_dot_duration__pb2 from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 +from google.protobuf import wrappers_pb2 as google_dot_protobuf_dot_wrappers__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n2buf/validate/conformance/cases/wkt_timestamp.proto\x12\x1e\x62uf.validate.conformance.cases\x1a\x1b\x62uf/validate/validate.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"=\n\rTimestampNone\x12,\n\x03val\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.TimestampR\x03val\"I\n\x11TimestampRequired\x12\x34\n\x03val\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.TimestampB\x06\xbaH\x03\xc8\x01\x01R\x03val\"J\n\x0eTimestampConst\x12\x38\n\x03val\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.TimestampB\n\xbaH\x07\xb2\x01\x04\x12\x02\x08\x03R\x03val\"E\n\x0bTimestampLT\x12\x36\n\x03val\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.TimestampB\x08\xbaH\x05\xb2\x01\x02\x1a\x00R\x03val\"H\n\x0cTimestampLTE\x12\x38\n\x03val\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.TimestampB\n\xbaH\x07\xb2\x01\x04\"\x02\x08\x01R\x03val\"H\n\x0bTimestampGT\x12\x39\n\x03val\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.TimestampB\x0b\xbaH\x08\xb2\x01\x05*\x03\x10\xe8\x07R\x03val\"J\n\x0cTimestampGTE\x12:\n\x03val\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.TimestampB\x0c\xbaH\t\xb2\x01\x06\x32\x04\x10\xc0\x84=R\x03val\"K\n\rTimestampGTLT\x12:\n\x03val\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.TimestampB\x0c\xbaH\t\xb2\x01\x06\x1a\x02\x08\x01*\x00R\x03val\"M\n\x0fTimestampExLTGT\x12:\n\x03val\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.TimestampB\x0c\xbaH\t\xb2\x01\x06\x1a\x00*\x02\x08\x01R\x03val\"P\n\x0fTimestampGTELTE\x12=\n\x03val\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.TimestampB\x0f\xbaH\x0c\xb2\x01\t\"\x03\x08\x90\x1c\x32\x02\x08 None: ... + +class TimestampWrongTypeScalar(_message.Message): + __slots__ = ("val",) + VAL_FIELD_NUMBER: _ClassVar[int] + val: int + def __init__(self, val: _Optional[int] = ...) -> None: ... + +class TimestampWrongTypeMessage(_message.Message): + __slots__ = ("val",) + class WrongType(_message.Message): + __slots__ = ("val",) + VAL_FIELD_NUMBER: _ClassVar[int] + val: int + def __init__(self, val: _Optional[int] = ...) -> None: ... + VAL_FIELD_NUMBER: _ClassVar[int] + val: TimestampWrongTypeMessage.WrongType + def __init__(self, val: _Optional[_Union[TimestampWrongTypeMessage.WrongType, _Mapping]] = ...) -> None: ... + +class TimestampWrongTypeWrapper(_message.Message): + __slots__ = ("val",) + VAL_FIELD_NUMBER: _ClassVar[int] + val: _wrappers_pb2.Int32Value + def __init__(self, val: _Optional[_Union[_wrappers_pb2.Int32Value, _Mapping]] = ...) -> None: ... + +class TimestampWrongTypeWKT(_message.Message): + __slots__ = ("val",) + VAL_FIELD_NUMBER: _ClassVar[int] + val: _duration_pb2.Duration + def __init__(self, val: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ...) -> None: ... From 96e5e2a66507287b046e48bdb32ca2f4280ae9cb Mon Sep 17 00:00:00 2001 From: Steve Ayers Date: Wed, 4 Jun 2025 12:26:30 -0400 Subject: [PATCH 2/5] Format --- protovalidate/internal/rules.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/protovalidate/internal/rules.py b/protovalidate/internal/rules.py index e9943ffa..b8a9e574 100644 --- a/protovalidate/internal/rules.py +++ b/protovalidate/internal/rules.py @@ -23,6 +23,9 @@ from buf.validate import validate_pb2 # type: ignore from protovalidate.internal.cel_field_presence import InterpretedRunner, in_has +# Convenience to stringify the type names for error messages +FIELD_TYPE_NAMES = {v: k for k, v in vars(descriptor.FieldDescriptor).items() if k.startswith("TYPE_")} + class CompilationError(Exception): pass @@ -397,7 +400,15 @@ def check_field_type(field: descriptor.FieldDescriptor, expected: int, wrapper_n if field.type != expected and ( field.type != descriptor.FieldDescriptor.TYPE_MESSAGE or field.message_type.full_name != wrapper_name ): - msg = f"field {field.name} has type {field.type} but expected {expected}" + field_type_str = FIELD_TYPE_NAMES[field.type] + if expected == 0: + if wrapper_name is not None: + expected_type_str = wrapper_name + else: + expected_type_str = FIELD_TYPE_NAMES[descriptor.FieldDescriptor.TYPE_MESSAGE] + else: + expected_type_str = FIELD_TYPE_NAMES[expected] + msg = f"field {field.name} has type {field_type_str} but expected {expected_type_str}" raise CompilationError(msg) @@ -821,6 +832,7 @@ def _new_scalar_field_rule( if field_level.ignore == validate_pb2.IGNORE_ALWAYS: return None type_case = field_level.WhichOneof("type") + # print(f"type case is {type_case}") if type_case is None: result = FieldRules(self._env, self._funcs, field, field_level, for_items=for_items) return result @@ -929,7 +941,7 @@ def _new_scalar_field_rule( result = FieldRules(self._env, self._funcs, field, field_level, for_items=for_items) return result elif type_case == "any": - check_field_type(field, descriptor.FieldDescriptor.TYPE_MESSAGE, "google.protobuf.Any") + check_field_type(field, 0, "google.protobuf.Any") result = AnyRules(self._env, self._funcs, field, field_level) return result From 08bd69fdf39b1727c90eef16fbcc611544c71684 Mon Sep 17 00:00:00 2001 From: Steve Ayers Date: Wed, 4 Jun 2025 12:28:17 -0400 Subject: [PATCH 3/5] Remove print --- protovalidate/internal/rules.py | 1 - 1 file changed, 1 deletion(-) diff --git a/protovalidate/internal/rules.py b/protovalidate/internal/rules.py index b8a9e574..7a9c1afb 100644 --- a/protovalidate/internal/rules.py +++ b/protovalidate/internal/rules.py @@ -832,7 +832,6 @@ def _new_scalar_field_rule( if field_level.ignore == validate_pb2.IGNORE_ALWAYS: return None type_case = field_level.WhichOneof("type") - # print(f"type case is {type_case}") if type_case is None: result = FieldRules(self._env, self._funcs, field, field_level, for_items=for_items) return result From b7e1cfaefca31617b70ba1a4205fca72e0068857 Mon Sep 17 00:00:00 2001 From: Steve Ayers Date: Thu, 5 Jun 2025 13:32:03 -0400 Subject: [PATCH 4/5] Feedback --- protovalidate/internal/rules.py | 116 ++++++++++++++++++-------------- 1 file changed, 66 insertions(+), 50 deletions(-) diff --git a/protovalidate/internal/rules.py b/protovalidate/internal/rules.py index 7a9c1afb..dc780bf9 100644 --- a/protovalidate/internal/rules.py +++ b/protovalidate/internal/rules.py @@ -23,9 +23,6 @@ from buf.validate import validate_pb2 # type: ignore from protovalidate.internal.cel_field_presence import InterpretedRunner, in_has -# Convenience to stringify the type names for error messages -FIELD_TYPE_NAMES = {v: k for k, v in vars(descriptor.FieldDescriptor).items() if k.startswith("TYPE_")} - class CompilationError(Exception): pass @@ -61,30 +58,6 @@ def unwrap(msg: message.Message) -> celtypes.Value: } -class MessageType(celtypes.MapType): - msg: message.Message - desc: descriptor.Descriptor - - def __init__(self, msg: message.Message): - super().__init__() - self.msg = msg - self.desc = msg.DESCRIPTOR - field: descriptor.FieldDescriptor - for field in self.desc.fields: - if field.containing_oneof is not None and not self.msg.HasField(field.name): - continue - self[field.name] = field_to_cel(self.msg, field) - - def __getitem__(self, name): - field = self.desc.fields_by_name[name] - if field.has_presence and not self.msg.HasField(name): - if in_has(): - raise KeyError() - else: - return _zero_value(field) - return super().__getitem__(name) - - def _msg_to_cel(msg: message.Message) -> celtypes.Value: ctor = _MSG_TYPE_URL_TO_CTOR.get(msg.DESCRIPTOR.full_name) if ctor is not None: @@ -92,28 +65,47 @@ def _msg_to_cel(msg: message.Message) -> celtypes.Value: return MessageType(msg) -_TYPE_TO_CTOR: dict[str, typing.Callable[..., celtypes.Value]] = { - descriptor.FieldDescriptor.TYPE_MESSAGE: _msg_to_cel, - descriptor.FieldDescriptor.TYPE_GROUP: _msg_to_cel, - descriptor.FieldDescriptor.TYPE_ENUM: celtypes.IntType, - descriptor.FieldDescriptor.TYPE_BOOL: celtypes.BoolType, - descriptor.FieldDescriptor.TYPE_BYTES: celtypes.BytesType, - descriptor.FieldDescriptor.TYPE_STRING: celtypes.StringType, - descriptor.FieldDescriptor.TYPE_FLOAT: celtypes.DoubleType, - descriptor.FieldDescriptor.TYPE_DOUBLE: celtypes.DoubleType, - descriptor.FieldDescriptor.TYPE_INT32: celtypes.IntType, - descriptor.FieldDescriptor.TYPE_INT64: celtypes.IntType, - descriptor.FieldDescriptor.TYPE_UINT32: celtypes.UintType, - descriptor.FieldDescriptor.TYPE_UINT64: celtypes.UintType, - descriptor.FieldDescriptor.TYPE_SINT32: celtypes.IntType, - descriptor.FieldDescriptor.TYPE_SINT64: celtypes.IntType, - descriptor.FieldDescriptor.TYPE_FIXED32: celtypes.UintType, - descriptor.FieldDescriptor.TYPE_FIXED64: celtypes.UintType, - descriptor.FieldDescriptor.TYPE_SFIXED32: celtypes.IntType, - descriptor.FieldDescriptor.TYPE_SFIXED64: celtypes.IntType, +class FieldDescMetadata(typing.TypedDict): + name: str + ctor: typing.Callable[..., celtypes.Value] + + +_FIELD_DESC_METADATA_MAP: dict[typing.Any, FieldDescMetadata] = { + descriptor.FieldDescriptor.TYPE_MESSAGE: {"name": "message", "ctor": _msg_to_cel}, + descriptor.FieldDescriptor.TYPE_GROUP: {"name": "group", "ctor": _msg_to_cel}, + descriptor.FieldDescriptor.TYPE_ENUM: {"name": "enum", "ctor": celtypes.IntType}, + descriptor.FieldDescriptor.TYPE_BOOL: {"name": "bool", "ctor": celtypes.BoolType}, + descriptor.FieldDescriptor.TYPE_BYTES: {"name": "bytes", "ctor": celtypes.BytesType}, + descriptor.FieldDescriptor.TYPE_STRING: {"name": "string", "ctor": celtypes.StringType}, + descriptor.FieldDescriptor.TYPE_FLOAT: {"name": "float", "ctor": celtypes.DoubleType}, + descriptor.FieldDescriptor.TYPE_DOUBLE: {"name": "double", "ctor": celtypes.DoubleType}, + descriptor.FieldDescriptor.TYPE_INT32: {"name": "int32", "ctor": celtypes.IntType}, + descriptor.FieldDescriptor.TYPE_INT64: {"name": "int64", "ctor": celtypes.IntType}, + descriptor.FieldDescriptor.TYPE_SINT32: {"name": "sint32", "ctor": celtypes.IntType}, + descriptor.FieldDescriptor.TYPE_SINT64: {"name": "sint64", "ctor": celtypes.IntType}, + descriptor.FieldDescriptor.TYPE_SFIXED32: {"name": "sfixed32", "ctor": celtypes.IntType}, + descriptor.FieldDescriptor.TYPE_SFIXED64: {"name": "sfixed64", "ctor": celtypes.IntType}, + descriptor.FieldDescriptor.TYPE_UINT32: {"name": "uint32", "ctor": celtypes.UintType}, + descriptor.FieldDescriptor.TYPE_UINT64: {"name": "uint64", "ctor": celtypes.UintType}, + descriptor.FieldDescriptor.TYPE_FIXED32: {"name": "fixed32", "ctor": celtypes.UintType}, + descriptor.FieldDescriptor.TYPE_FIXED64: {"name": "fixed64", "ctor": celtypes.UintType}, } +def _get_type_name(fd: typing.Any) -> str: + md = _FIELD_DESC_METADATA_MAP.get(fd) + if md is None: + return "unknown" + return md["name"] + + +def _get_type_ctor(fd: typing.Any) -> typing.Optional[typing.Callable[..., celtypes.Value]]: + md = _FIELD_DESC_METADATA_MAP.get(fd) + if md is None: + return None + return md["ctor"] + + def _proto_message_has_field(msg: message.Message, field: descriptor.FieldDescriptor) -> typing.Any: if field.is_extension: return msg.HasExtension(field) # type: ignore @@ -129,7 +121,7 @@ def _proto_message_get_field(msg: message.Message, field: descriptor.FieldDescri def _scalar_field_value_to_cel(val: typing.Any, field: descriptor.FieldDescriptor) -> celtypes.Value: - ctor = _TYPE_TO_CTOR.get(field.type) + ctor = _get_type_ctor(field.type) if ctor is None: msg = "unknown field type" raise CompilationError(msg) @@ -234,6 +226,30 @@ def _set_path_element_map_key( raise CompilationError(msg) +class MessageType(celtypes.MapType): + msg: message.Message + desc: descriptor.Descriptor + + def __init__(self, msg: message.Message): + super().__init__() + self.msg = msg + self.desc = msg.DESCRIPTOR + field: descriptor.FieldDescriptor + for field in self.desc.fields: + if field.containing_oneof is not None and not self.msg.HasField(field.name): + continue + self[field.name] = field_to_cel(self.msg, field) + + def __getitem__(self, name): + field = self.desc.fields_by_name[name] + if field.has_presence and not self.msg.HasField(name): + if in_has(): + raise KeyError() + else: + return _zero_value(field) + return super().__getitem__(name) + + class Violation: """A singular rule violation.""" @@ -400,14 +416,14 @@ def check_field_type(field: descriptor.FieldDescriptor, expected: int, wrapper_n if field.type != expected and ( field.type != descriptor.FieldDescriptor.TYPE_MESSAGE or field.message_type.full_name != wrapper_name ): - field_type_str = FIELD_TYPE_NAMES[field.type] + field_type_str = _get_type_name(field.type) if expected == 0: if wrapper_name is not None: expected_type_str = wrapper_name else: - expected_type_str = FIELD_TYPE_NAMES[descriptor.FieldDescriptor.TYPE_MESSAGE] + expected_type_str = _get_type_name(descriptor.FieldDescriptor.TYPE_MESSAGE) else: - expected_type_str = FIELD_TYPE_NAMES[expected] + expected_type_str = _get_type_name(expected) msg = f"field {field.name} has type {field_type_str} but expected {expected_type_str}" raise CompilationError(msg) From be9a141fad4d088a22711374696b6f4927553e15 Mon Sep 17 00:00:00 2001 From: Steve Ayers Date: Thu, 5 Jun 2025 13:34:27 -0400 Subject: [PATCH 5/5] Feedback --- protovalidate/internal/rules.py | 48 ++++++++++++++++----------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/protovalidate/internal/rules.py b/protovalidate/internal/rules.py index dc780bf9..96842245 100644 --- a/protovalidate/internal/rules.py +++ b/protovalidate/internal/rules.py @@ -58,6 +58,30 @@ def unwrap(msg: message.Message) -> celtypes.Value: } +class MessageType(celtypes.MapType): + msg: message.Message + desc: descriptor.Descriptor + + def __init__(self, msg: message.Message): + super().__init__() + self.msg = msg + self.desc = msg.DESCRIPTOR + field: descriptor.FieldDescriptor + for field in self.desc.fields: + if field.containing_oneof is not None and not self.msg.HasField(field.name): + continue + self[field.name] = field_to_cel(self.msg, field) + + def __getitem__(self, name): + field = self.desc.fields_by_name[name] + if field.has_presence and not self.msg.HasField(name): + if in_has(): + raise KeyError() + else: + return _zero_value(field) + return super().__getitem__(name) + + def _msg_to_cel(msg: message.Message) -> celtypes.Value: ctor = _MSG_TYPE_URL_TO_CTOR.get(msg.DESCRIPTOR.full_name) if ctor is not None: @@ -226,30 +250,6 @@ def _set_path_element_map_key( raise CompilationError(msg) -class MessageType(celtypes.MapType): - msg: message.Message - desc: descriptor.Descriptor - - def __init__(self, msg: message.Message): - super().__init__() - self.msg = msg - self.desc = msg.DESCRIPTOR - field: descriptor.FieldDescriptor - for field in self.desc.fields: - if field.containing_oneof is not None and not self.msg.HasField(field.name): - continue - self[field.name] = field_to_cel(self.msg, field) - - def __getitem__(self, name): - field = self.desc.fields_by_name[name] - if field.has_presence and not self.msg.HasField(name): - if in_has(): - raise KeyError() - else: - return _zero_value(field) - return super().__getitem__(name) - - class Violation: """A singular rule violation."""