From a2fe978d3143ee7ee4a1c26fa1432cc3898eda7a Mon Sep 17 00:00:00 2001 From: Stefan VanBuren Date: Tue, 18 Feb 2025 13:19:18 -0500 Subject: [PATCH 1/3] Pass `--strict_message` conformance (minus duration/timestamp) We can't currently pass the conformance tests involving duration+timestamp types, as cel-python does not support nanoseconds in its duration implementation (linked issue). Otherwise, this gets us to a conformant spot. Related to #255. --- Makefile | 2 +- protovalidate/internal/string_format.py | 16 +++++++++++++++- tests/conformance/nonconforming.yaml | 8 +++++++- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 8c89c02c..8fa32085 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ export PATH := $(BIN):$(PATH) export GOBIN := $(abspath $(BIN)) # Set to use a different Python interpreter. For example, `PYTHON=python make test`. PYTHON ?= python3 -CONFORMANCE_ARGS ?= --strict --expected_failures=tests/conformance/nonconforming.yaml --timeout 10s +CONFORMANCE_ARGS ?= --strict --strict_message --expected_failures=tests/conformance/nonconforming.yaml --timeout 10s ADD_LICENSE_HEADER := $(BIN)/license-header \ --license-type apache \ --copyright-holder "Buf Technologies, Inc." \ diff --git a/protovalidate/internal/string_format.py b/protovalidate/internal/string_format.py index 6604029b..698b6d21 100644 --- a/protovalidate/internal/string_format.py +++ b/protovalidate/internal/string_format.py @@ -146,9 +146,19 @@ def format_string(self, arg: celtypes.Value) -> celpy.Result: if isinstance(arg, celtypes.StringType): return arg if isinstance(arg, celtypes.BytesType): - return celtypes.StringType(arg.hex()) + return celtypes.StringType(arg) if isinstance(arg, celtypes.ListType): return self.format_list(arg) + if isinstance(arg, celtypes.BoolType): + # True -> true + return celtypes.StringType(str(arg).lower()) + if isinstance(arg, celtypes.DoubleType): + return celtypes.StringType(f"{arg:.0f}") + if isinstance(arg, celtypes.TimestampType): + base = arg.isoformat() + if arg.getMilliseconds() != 0: + base = arg.isoformat(timespec="milliseconds") + return celtypes.StringType(base.removesuffix("+00:00") + "Z") return celtypes.StringType(arg) def format_value(self, arg: celtypes.Value) -> celpy.Result: @@ -156,6 +166,10 @@ def format_value(self, arg: celtypes.Value) -> celpy.Result: return celtypes.StringType(quote(arg)) if isinstance(arg, celtypes.UintType): return celtypes.StringType(arg) + if isinstance(arg, celtypes.DurationType): + return celtypes.StringType(f'duration("{arg.seconds + (arg.microseconds // 1e6):.0f}s")') + if isinstance(arg, celtypes.DoubleType): + return celtypes.StringType(f"{arg:f}") return self.format_string(arg) def format_list(self, arg: celtypes.ListType) -> celpy.Result: diff --git a/tests/conformance/nonconforming.yaml b/tests/conformance/nonconforming.yaml index 4abf19b8..25ba84db 100644 --- a/tests/conformance/nonconforming.yaml +++ b/tests/conformance/nonconforming.yaml @@ -1,8 +1,14 @@ # celpy doesn't support nano seconds +# ref: https://github.com/cloud-custodian/cel-python/issues/43 standard_constraints/well_known_types/duration: - gte_lte/invalid/above - lte/invalid - - not in/valid + - gte/invalid + - gt/invalid + - in/invalid + - "not in/valid" standard_constraints/well_known_types/timestamp: - gte_lte/invalid/above - lte/invalid +standard_constraints/repeated: + - duration/gte/invalid From 72d7ffa4d8dc4b50a3c9a580c78faeaba73412a9 Mon Sep 17 00:00:00 2001 From: Stefan VanBuren Date: Tue, 18 Feb 2025 15:41:10 -0500 Subject: [PATCH 2/3] Fix duration formatting Still issues with tests using below-microsecond nanosecond values, but these tests use millisecond values, so we can fix them up. --- protovalidate/internal/string_format.py | 8 +++++++- tests/conformance/nonconforming.yaml | 7 +------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/protovalidate/internal/string_format.py b/protovalidate/internal/string_format.py index 698b6d21..22a2f74e 100644 --- a/protovalidate/internal/string_format.py +++ b/protovalidate/internal/string_format.py @@ -14,6 +14,7 @@ import celpy # type: ignore from celpy import celtypes # type: ignore +from decimal import Decimal QUOTE_TRANS = str.maketrans( { @@ -154,6 +155,8 @@ def format_string(self, arg: celtypes.Value) -> celpy.Result: return celtypes.StringType(str(arg).lower()) if isinstance(arg, celtypes.DoubleType): return celtypes.StringType(f"{arg:.0f}") + if isinstance(arg, celtypes.DurationType): + return celtypes.StringType(self._format_duration(arg)) if isinstance(arg, celtypes.TimestampType): base = arg.isoformat() if arg.getMilliseconds() != 0: @@ -167,7 +170,7 @@ def format_value(self, arg: celtypes.Value) -> celpy.Result: if isinstance(arg, celtypes.UintType): return celtypes.StringType(arg) if isinstance(arg, celtypes.DurationType): - return celtypes.StringType(f'duration("{arg.seconds + (arg.microseconds // 1e6):.0f}s")') + return celtypes.StringType(f'duration("{self._format_duration(arg)}")') if isinstance(arg, celtypes.DoubleType): return celtypes.StringType(f"{arg:f}") return self.format_string(arg) @@ -181,6 +184,9 @@ def format_list(self, arg: celtypes.ListType) -> celpy.Result: result += "]" return celtypes.StringType(result) + def _format_duration(self, arg: celtypes.DurationType) -> celpy.Result: + return f"{arg.seconds + Decimal(arg.microseconds) / Decimal(1_000_000):f}s" + _default_format = StringFormat("en_US") format = _default_format.format # noqa: A001 diff --git a/tests/conformance/nonconforming.yaml b/tests/conformance/nonconforming.yaml index 25ba84db..25744e50 100644 --- a/tests/conformance/nonconforming.yaml +++ b/tests/conformance/nonconforming.yaml @@ -3,12 +3,7 @@ standard_constraints/well_known_types/duration: - gte_lte/invalid/above - lte/invalid - - gte/invalid - - gt/invalid - - in/invalid - - "not in/valid" + - not in/valid standard_constraints/well_known_types/timestamp: - gte_lte/invalid/above - lte/invalid -standard_constraints/repeated: - - duration/gte/invalid From 2d699faaadc3eda2a67d2c519e95f84f2a036a64 Mon Sep 17 00:00:00 2001 From: Stefan VanBuren Date: Tue, 18 Feb 2025 15:45:10 -0500 Subject: [PATCH 3/3] Fix import ordering --- protovalidate/internal/string_format.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/protovalidate/internal/string_format.py b/protovalidate/internal/string_format.py index 22a2f74e..7043084f 100644 --- a/protovalidate/internal/string_format.py +++ b/protovalidate/internal/string_format.py @@ -12,9 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +from decimal import Decimal + import celpy # type: ignore from celpy import celtypes # type: ignore -from decimal import Decimal QUOTE_TRANS = str.maketrans( {