diff --git a/MODULE.bazel b/MODULE.bazel index 226a472..e51fbae 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -14,10 +14,10 @@ bazel_dep(name = "bazel_skylib", version = "1.8.2") # https://registry.bazel.build/modules/cel-cpp bazel_dep(name = "cel-cpp", version = "0.14.0", repo_name = "com_google_cel_cpp") -# 12/30/2025 -_CEL_CPP_COMMIT = "13b249f589ab3e22dee5b384169cb7dd50804b36" +# 03/25/2026 +_CEL_CPP_COMMIT = "f6cd0c895e42954475b3734c867e8902557d1b37" -_CEL_CPP_SHA256 = "c7596db4538722e78f9c7f7ae704a15fd48d5e84254d717b5b558c3b1b0169fa" +_CEL_CPP_SHA256 = "8af44a84983f190e30ba4e5064ed5f2a2bbd13988a50b5c75288096636f1548b" archive_override( module_name = "cel-cpp", diff --git a/cel_expr_python/BUILD b/cel_expr_python/BUILD index 5ae3128..8aa4390 100644 --- a/cel_expr_python/BUILD +++ b/cel_expr_python/BUILD @@ -18,6 +18,8 @@ pybind_extension( "py_cel_arena.h", "py_cel_env.cc", "py_cel_env.h", + "py_cel_env_config.cc", + "py_cel_env_config.h", "py_cel_env_internal.cc", "py_cel_env_internal.h", "py_cel_expression.cc", @@ -76,6 +78,8 @@ pybind_extension( "@com_google_cel_cpp//compiler", "@com_google_cel_cpp//compiler:compiler_factory", "@com_google_cel_cpp//compiler:standard_library", + "@com_google_cel_cpp//env:config", + "@com_google_cel_cpp//env:env_yaml", "@com_google_cel_cpp//extensions/protobuf:runtime_adapter", "@com_google_cel_cpp//parser:parser_interface", "@com_google_cel_cpp//runtime", @@ -126,3 +130,12 @@ py_test( "@com_google_protobuf//:protobuf", ], ) + +py_test( + name = "cel_env_test", + srcs = ["cel_env_test.py"], + deps = [ + ":cel", + "@com_google_absl_py//absl/testing:absltest", + ], +) diff --git a/cel_expr_python/cel_env_test.py b/cel_expr_python/cel_env_test.py new file mode 100644 index 0000000..46c5cf2 --- /dev/null +++ b/cel_expr_python/cel_env_test.py @@ -0,0 +1,117 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tests for Env/Config. + +This module contains tests for the `cel.EnvConfig` class, focusing on its +ability to be created from and serialized to YAML format. +""" + +from absl.testing import absltest +from cel_expr_python import cel + + +class CelEnvTest(absltest.TestCase): + + def test_env_config_from_and_to_yaml(self): + config = cel.NewEnvConfigFromYaml(""" + name: foo + container: test.container + stdlib: + exclude_macros: + - map + - filter + exclude_functions: + - name: "_+_" + extensions: + - name: math + variables: + - name: one + type_name: int + value: 1 + functions: + - name: add + overloads: + - id: "add_int_int" + args: + - type_name: int + - type_name: int + return: + type_name: int + """) + yaml = config.to_yaml() + self.assertEqual( + normalize_yaml(yaml), + normalize_yaml(""" + name: "foo" + container: "test.container" + extensions: + - name: "math" + stdlib: + exclude_macros: + - "filter" + - "map" + exclude_functions: + - name: "_+_" + variables: + - name: "one" + type_name: "int" + value: 1 + functions: + - name: "add" + overloads: + - id: "add_int_int" + args: + - type_name: "int" + - type_name: "int" + return: + type_name: "int" + """), + ) + + def test_invalid_yaml(self): + with self.assertRaises(Exception) as e: + cel.NewEnvConfigFromYaml(" invalid yaml") + self.assertIn( + "1:2: Invalid CEL environment config YAML\n" + "| invalid yaml\n" + "| ^", + str(e.exception), + ) + + +def normalize_yaml(yaml: str) -> str: + lines = yaml.split("\n") + indent = -1 + unindented_lines = [] + for line in lines: + pos = -1 + for i, char in enumerate(line): + if char != " " and char != "\t": + pos = i + break + if pos == -1: + # Skip blank lines. + continue + if indent == -1: + indent = pos + if pos >= indent: + unindented_lines.append(line[indent:]) + else: + unindented_lines.append(line) + return "\n".join(unindented_lines) + + +if __name__ == "__main__": + absltest.main() diff --git a/cel_expr_python/py_cel_env_config.cc b/cel_expr_python/py_cel_env_config.cc new file mode 100644 index 0000000..1f4014f --- /dev/null +++ b/cel_expr_python/py_cel_env_config.cc @@ -0,0 +1,50 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "cel_expr_python/py_cel_env_config.h" + +#include +#include +#include + +#include "env/env_yaml.h" +#include "cel_expr_python/py_error_status.h" +#include + +namespace cel_python { + +namespace py = ::pybind11; + +void PyCelEnvConfig::DefinePythonBindings(pybind11::module& m) { + py::class_> cel_class( + m, "EnvConfig"); + m.def("NewEnvConfigFromYaml", &PyCelEnvConfig::FromYaml, py::arg("yaml")); + + cel_class.def("to_yaml", &PyCelEnvConfig::ToYaml); +} + +PyCelEnvConfig PyCelEnvConfig::FromYaml(std::string yaml) { + PyCelEnvConfig config; + config.config_ = ThrowIfError(cel::EnvConfigFromYaml(yaml)); + return config; +} + +std::string PyCelEnvConfig::ToYaml() const { + std::stringstream ss; + cel::EnvConfigToYaml(config_, ss); + return ss.str(); +} + +} // namespace cel_python diff --git a/cel_expr_python/py_cel_env_config.h b/cel_expr_python/py_cel_env_config.h new file mode 100644 index 0000000..5e28628 --- /dev/null +++ b/cel_expr_python/py_cel_env_config.h @@ -0,0 +1,39 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef THIRD_PARTY_CEL_PYTHON_PY_CEL_ENV_CONFIG_H_ +#define THIRD_PARTY_CEL_PYTHON_PY_CEL_ENV_CONFIG_H_ + +#include + +#include "env/config.h" +#include + +namespace cel_python { + +class PyCelEnvConfig { + public: + static void DefinePythonBindings(pybind11::module& m); + + static PyCelEnvConfig FromYaml(std::string yaml); + std::string ToYaml() const; + + private: + cel::Config config_; +}; + +} // namespace cel_python + +#endif // THIRD_PARTY_CEL_PYTHON_PY_CEL_ENV_CONFIG_H_ diff --git a/cel_expr_python/py_cel_module.cc b/cel_expr_python/py_cel_module.cc index 9698d52..d6766fe 100644 --- a/cel_expr_python/py_cel_module.cc +++ b/cel_expr_python/py_cel_module.cc @@ -15,6 +15,7 @@ #include "cel_expr_python/py_cel_activation.h" #include "cel_expr_python/py_cel_arena.h" #include "cel_expr_python/py_cel_env.h" +#include "cel_expr_python/py_cel_env_config.h" #include "cel_expr_python/py_cel_expression.h" #include "cel_expr_python/py_cel_function.h" #include "cel_expr_python/py_cel_function_decl.h" @@ -38,6 +39,7 @@ PYBIND11_MODULE(cel, m) { PyCelFunctionDecl::DefinePythonBindings(m); PyCelPythonExtension::DefinePythonBindings(m); PyCelFunction::DefinePythonBindings(m); + PyCelEnvConfig::DefinePythonBindings(m); PyCelEnv::DefinePythonBindings(m); }