From 3a0e759da65439821fa18ed1d2b7a527a2eaabc8 Mon Sep 17 00:00:00 2001 From: Jonathan Tatum Date: Thu, 5 Nov 2026 15:46:49 -0800 Subject: [PATCH] Fix cel-python type(...) tests and run cel-python TAP tests on cel-cpp changes. PiperOrigin-RevId: 1015048185 --- cel_expr_python/cel_test.py | 20 +++++++++++--------- conformance/conformance_test.py | 16 ++++++++-------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/cel_expr_python/cel_test.py b/cel_expr_python/cel_test.py index c04806f..39f8564 100644 --- a/cel_expr_python/cel_test.py +++ b/cel_expr_python/cel_test.py @@ -681,25 +681,27 @@ def testTypeType(self): cel.Type.Type(cel.Type.STRING).is_assignable_from(expr.return_type()) ) - # The following two tests demonstrate a quirk of CEL assignability - # rules. In the case of type checking, `type(1)` is deemed to be - # *not* assignable to Type.STRING. However, in runtime use, it would be seen - # as assignable. + # When the branches of a conditional produce type values whose parameters + # are not co-assignable, the checker unifies them to `type(dyn)`. + expr = self.env.compile("var_bool ? type('a') : type(1)") + self.assertEqual(expr.return_type(), cel.Type.Type(cel.Type.DYN)) + self.assertFalse( + cel.Type.Type(cel.Type.STRING).is_assignable_from(expr.return_type()) + ) + res = self._eval( "var_bool ? type('a') : type(1)", {"var_bool": True}, - expected_return_type=cel.Type.Type(cel.Type.STRING), + expected_return_type=cel.Type.Type(cel.Type.DYN), ) self.assertEqual(res.value(), cel.Type.STRING) res = self._eval( "var_bool ? type('a') : type(1)", {"var_bool": False}, - expected_return_type=cel.Type.Type(cel.Type.STRING), + expected_return_type=cel.Type.Type(cel.Type.DYN), ) - self.assertEqual( - res.value(), cel.Type.INT - ) # This behavior is counterintuitive but works as implemented. + self.assertEqual(res.value(), cel.Type.INT) def testTypeInitSignature(self): self.assertEqual(cel.Type("int"), cel.Type.INT) diff --git a/conformance/conformance_test.py b/conformance/conformance_test.py index da54e88..0bbff08 100644 --- a/conformance/conformance_test.py +++ b/conformance/conformance_test.py @@ -78,13 +78,6 @@ class ConformanceTestSuite(unittest.TestSuite): "proto3/set_null/map_timestamp_null_pruned", "proto3/set_null/map_duration_null_pruned", "proto3/set_null/map_wrapper_null_pruned", - # TODO(b/577138668): C++ type checker bug, though lower priority. - "type_deduction/type_decay/type_list_decay", - "type_deduction/type_decay/type_ternary_decay", - "type_deduction/type_decay/type_free_param_list_decay", - "type_deduction/type_decay/type_free_param_list_decay_order_independent", - "type_deduction/type_decay/type_nested_list_decay", - "type_deduction/type_decay/type_free_param_list_concat", # Skip until duration/timestamp range and proto wrapper validation is updated for spec parity. "timestamps/timestamp_arithmetic/add_duration_to_duration_max", "timestamps/timestamp_arithmetic/subtract_duration_from_duration_min", @@ -317,8 +310,13 @@ def _convert_type(self, tp: checked_pb.Type) -> cel.Type: for param in tp.abstract_type.parameter_types ], ) + elif kind == "type": + # An unset type parameter denotes the unparameterized `type`. + if tp.type.WhichOneof("type_kind") is None: + return cel.Type.TYPE + return cel.Type.Type(self._convert_type(tp.type)) else: - self.fail("Unsupported type: " + str(type)) + self.fail("Unsupported type: " + str(tp)) def _convert_value(self, value: value_pb.Value) -> Any: if value.HasField("null_value"): @@ -503,6 +501,8 @@ def assertType(self, result_type: cel.Type, expected_type: checked_pb.Type): ], ), ) + elif kind == "type": + self.assertEqual(result_type, self._convert_type(expected_type)) else: self.fail("Unsupported type validation: " + str(expected_type))