Skip to content

Commit 98336d8

Browse files
author
John Chadwick
committed
Implement getField CEL function
I'm proposing this as an eventual replacement (before 1.0) of our hack around the fact that the `in` identifier is reserved in CEL. This is especially urgent for protovalidate-cc which is currently carrying patches to the CEL implementation in order to enable it, since cel-cpp doesn't allow this sort of functionality to be added in at runtime.
1 parent 8979f49 commit 98336d8

File tree

2 files changed

+19
-4
lines changed

2 files changed

+19
-4
lines changed

protovalidate/internal/constraints.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def make_timestamp(msg: message.Message) -> celtypes.TimestampType:
4040

4141

4242
def unwrap(msg: message.Message) -> celtypes.Value:
43-
return _field_to_cel(msg, msg.DESCRIPTOR.fields_by_name["value"])
43+
return field_to_cel(msg, msg.DESCRIPTOR.fields_by_name["value"])
4444

4545

4646
_MSG_TYPE_URL_TO_CTOR: dict[str, typing.Callable[..., celtypes.Value]] = {
@@ -70,7 +70,7 @@ def __init__(self, msg: message.Message):
7070
for field in self.desc.fields:
7171
if field.containing_oneof is not None and not self.msg.HasField(field.name):
7272
continue
73-
self[field.name] = _field_to_cel(self.msg, field)
73+
self[field.name] = field_to_cel(self.msg, field)
7474

7575
def __getitem__(self, name):
7676
field = self.desc.fields_by_name[name]
@@ -175,7 +175,7 @@ def _map_field_to_cel(msg: message.Message, field: descriptor.FieldDescriptor) -
175175
return _map_field_value_to_cel(_proto_message_get_field(msg, field), field)
176176

177177

178-
def _field_to_cel(msg: message.Message, field: descriptor.FieldDescriptor) -> celtypes.Value:
178+
def field_to_cel(msg: message.Message, field: descriptor.FieldDescriptor) -> celtypes.Value:
179179
if field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
180180
return _repeated_field_to_cel(msg, field)
181181
elif field.message_type is not None and not _proto_message_has_field(msg, field):
@@ -374,7 +374,7 @@ def add_rule(
374374
rule_cel = None
375375
if rule_field is not None and self._rules is not None:
376376
rule_value = _proto_message_get_field(self._rules, rule_field)
377-
rule_cel = _field_to_cel(self._rules, rule_field)
377+
rule_cel = field_to_cel(self._rules, rule_field)
378378
self._cel.append(
379379
CelRunner(
380380
runner=prog,

protovalidate/internal/extra_func.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from celpy import celtypes
2323

2424
from protovalidate.internal import string_format
25+
from protovalidate.internal.constraints import MessageType, field_to_cel
2526

2627

2728
def _validate_hostname(host):
@@ -112,6 +113,19 @@ def validate_ip(val: typing.Union[str, bytes], version: typing.Optional[int] = N
112113
return False
113114

114115

116+
def get_field(message: celtypes.Value, field_name: celtypes.Value) -> celpy.Result:
117+
if not isinstance(message, MessageType):
118+
msg = "invalid argument, expected message"
119+
raise celpy.CELEvalError(msg)
120+
if not isinstance(field_name, celtypes.StringType):
121+
msg = "invalid argument, expected string"
122+
raise celpy.CELEvalError(msg)
123+
if field_name not in message.desc.fields_by_name:
124+
msg = f"no such field: {field_name}"
125+
raise celpy.CELEvalError(msg)
126+
return field_to_cel(message.msg, message.desc.fields_by_name[field_name])
127+
128+
115129
def is_ip(val: celtypes.Value, version: typing.Optional[celtypes.Value] = None) -> celpy.Result:
116130
if not isinstance(val, (celtypes.BytesType, celtypes.StringType)):
117131
msg = "invalid argument, expected string or bytes"
@@ -245,6 +259,7 @@ def make_extra_funcs(locale: str) -> dict[str, celpy.CELFunction]:
245259
# Missing standard functions
246260
"format": string_fmt.format,
247261
# protovalidate specific functions
262+
"getField": get_field,
248263
"isNan": is_nan,
249264
"isInf": is_inf,
250265
"isIp": is_ip,

0 commit comments

Comments
 (0)