Skip to content

Commit 1ec5a73

Browse files
dmitriplotnikovcopybara-github
authored andcommitted
Integrate cel::Config::VariableConfig into Python CEL environment
This change allows the Python CEL environment to be configured using a `cel.Config` object, which can be loaded from YAML. Variables provided in the `cel.NewEnv` call are now used to augment the configuration. The underlying C++ implementation now uses `cel::Env` to configure the compiler and runtime builders. PiperOrigin-RevId: 891920215
1 parent 2e77bc8 commit 1ec5a73

11 files changed

Lines changed: 386 additions & 69 deletions

MODULE.bazel

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ bazel_dep(name = "bazel_skylib", version = "1.8.2")
1414
# https://registry.bazel.build/modules/cel-cpp
1515
bazel_dep(name = "cel-cpp", version = "0.14.0", repo_name = "com_google_cel_cpp")
1616

17-
# 03/25/2026
18-
_CEL_CPP_COMMIT = "f6cd0c895e42954475b3734c867e8902557d1b37"
17+
# 03/30/2026
18+
_CEL_CPP_COMMIT = "5a3463337cf2a9b90b53833af2bbc1f35da90d64"
1919

20-
_CEL_CPP_SHA256 = "8af44a84983f190e30ba4e5064ed5f2a2bbd13988a50b5c75288096636f1548b"
20+
_CEL_CPP_SHA256 = "299be398d1495340eb92da31f9a0667e1351479752e8a567ac31c385e4aea73c"
2121

2222
archive_override(
2323
module_name = "cel-cpp",

cel_expr_python/BUILD

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,7 @@ pybind_extension(
7676
"@com_google_cel_cpp//common:value",
7777
"@com_google_cel_cpp//common:value_kind",
7878
"@com_google_cel_cpp//compiler",
79-
"@com_google_cel_cpp//compiler:compiler_factory",
80-
"@com_google_cel_cpp//compiler:standard_library",
79+
"@com_google_cel_cpp//env",
8180
"@com_google_cel_cpp//env:config",
8281
"@com_google_cel_cpp//env:env_yaml",
8382
"@com_google_cel_cpp//extensions/protobuf:runtime_adapter",
@@ -136,6 +135,7 @@ py_test(
136135
srcs = ["cel_env_test.py"],
137136
deps = [
138137
":cel",
138+
"//testing:proto2_test_all_types_py_pb2",
139139
"@com_google_absl_py//absl/testing:absltest",
140140
],
141141
)

cel_expr_python/cel_env_test.py

Lines changed: 148 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
from absl.testing import absltest
2222
from cel_expr_python import cel
23+
from cel.expr.conformance.proto2 import test_all_types_pb2 as test_all_types_pb
2324

2425

2526
class CelEnvTest(absltest.TestCase):
@@ -85,11 +86,156 @@ def test_invalid_yaml(self):
8586
cel.NewEnvConfigFromYaml(" invalid yaml")
8687
self.assertIn(
8788
"1:2: Invalid CEL environment config YAML\n"
88-
"| invalid yaml\n"
89-
"| ^",
89+
+ "| invalid yaml\n"
90+
+ "| ^",
9091
str(e.exception),
9192
)
9293

94+
def test_config_export_variables(self):
95+
config = cel.NewEnv(
96+
variables={
97+
"var_bool": cel.Type.BOOL,
98+
"var_int": cel.Type.INT,
99+
"var_uint": cel.Type.UINT,
100+
"var_double": cel.Type.DOUBLE,
101+
"var_str": cel.Type.STRING,
102+
"var_bytes": cel.Type.BYTES,
103+
"var_msg": cel.Type("cel.expr.conformance.proto2.TestAllTypes"),
104+
"var_string_list": cel.Type.List(cel.Type.STRING),
105+
"var_timestamp": cel.Type.TIMESTAMP,
106+
"var_duration": cel.Type.DURATION,
107+
"var_dyn_list": cel.Type.LIST,
108+
"var_int_map": cel.Type.Map(cel.Type.INT, cel.Type.STRING),
109+
"var_string_map": cel.Type.Map(cel.Type.STRING, cel.Type.BOOL),
110+
"var_dyn_map": cel.Type.MAP,
111+
"var_dyn": cel.Type.DYN,
112+
}
113+
)
114+
yaml = config.config().to_yaml()
115+
self.assertEqual(
116+
normalize_yaml(yaml),
117+
normalize_yaml("""
118+
variables:
119+
- name: "var_bool"
120+
type_name: "bool"
121+
- name: "var_bytes"
122+
type_name: "bytes"
123+
- name: "var_double"
124+
type_name: "double"
125+
- name: "var_duration"
126+
type_name: "duration"
127+
- name: "var_dyn"
128+
type_name: "dyn"
129+
- name: "var_dyn_list"
130+
type_name: "list"
131+
params:
132+
- type_name: "dyn"
133+
- name: "var_dyn_map"
134+
type_name: "map"
135+
params:
136+
- type_name: "dyn"
137+
- type_name: "dyn"
138+
- name: "var_int"
139+
type_name: "int"
140+
- name: "var_int_map"
141+
type_name: "map"
142+
params:
143+
- type_name: "int"
144+
- type_name: "string"
145+
- name: "var_msg"
146+
type_name: "cel.expr.conformance.proto2.TestAllTypes"
147+
- name: "var_str"
148+
type_name: "string"
149+
- name: "var_string_list"
150+
type_name: "list"
151+
params:
152+
- type_name: "string"
153+
- name: "var_string_map"
154+
type_name: "map"
155+
params:
156+
- type_name: "string"
157+
- type_name: "bool"
158+
- name: "var_timestamp"
159+
type_name: "timestamp"
160+
- name: "var_uint"
161+
type_name: "uint"
162+
"""),
163+
)
164+
165+
def test_config_augmented_variables(self):
166+
config = cel.NewEnvConfigFromYaml("""
167+
variables:
168+
- name: "var_bool"
169+
type_name: "bool"
170+
""")
171+
env = cel.NewEnv(
172+
config=config,
173+
variables={
174+
"var_msg": cel.Type("cel.expr.conformance.proto2.TestAllTypes"),
175+
},
176+
)
177+
yaml = env.config().to_yaml()
178+
self.assertEqual(
179+
normalize_yaml(yaml),
180+
normalize_yaml("""
181+
variables:
182+
- name: "var_bool"
183+
type_name: "bool"
184+
- name: "var_msg"
185+
type_name: "cel.expr.conformance.proto2.TestAllTypes"
186+
"""),
187+
)
188+
189+
def test_config_variable_override(self):
190+
config = cel.NewEnvConfigFromYaml("""
191+
variables:
192+
- name: "var_bool"
193+
type_name: "bool"
194+
""")
195+
196+
with self.assertRaises(Exception) as e:
197+
cel.NewEnv(
198+
config=config,
199+
variables={
200+
"var_bool": cel.Type.INT,
201+
},
202+
)
203+
self.assertIn(
204+
"Variable 'var_bool' is already included",
205+
str(e.exception),
206+
)
207+
208+
def test_config_variable_types(self):
209+
config = cel.NewEnvConfigFromYaml("""
210+
variables:
211+
- name: "var_bool"
212+
type_name: "bool"
213+
- name: "var_int"
214+
type_name: "int"
215+
value: 42
216+
""")
217+
env = cel.NewEnv(
218+
config=config,
219+
variables={
220+
"var_msg": cel.Type("cel.expr.conformance.proto2.TestAllTypes"),
221+
},
222+
)
223+
data = {
224+
"var_bool": True,
225+
"var_msg": test_all_types_pb.TestAllTypes(single_string="hello"),
226+
}
227+
res = env.compile("var_bool").eval(data=data)
228+
self.assertEqual(res.type(), cel.Type.BOOL)
229+
self.assertTrue(res.value())
230+
231+
res = env.compile("var_msg.single_string").eval(data=data)
232+
self.assertEqual(res.type(), cel.Type.STRING)
233+
self.assertEqual(res.value(), "hello")
234+
235+
res = env.compile("var_int").eval(data=data)
236+
self.assertEqual(res.type(), cel.Type.INT)
237+
self.assertEqual(res.value(), 42)
238+
93239

94240
def normalize_yaml(yaml: str) -> str:
95241
lines = yaml.split("\n")

cel_expr_python/py_cel_env.cc

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@
2525
#include <vector>
2626

2727
#include "absl/log/absl_check.h"
28+
#include "env/config.h"
2829
#include "cel_expr_python/py_cel_activation.h"
2930
#include "cel_expr_python/py_cel_arena.h"
31+
#include "cel_expr_python/py_cel_env_config.h"
3032
#include "cel_expr_python/py_cel_env_internal.h"
3133
#include "cel_expr_python/py_cel_expression.h"
3234
#include "cel_expr_python/py_cel_type.h"
@@ -42,7 +44,7 @@ void PyCelEnv::DefinePythonBindings(pybind11::module& m) {
4244
py::class_<PyCelEnv, std::shared_ptr<PyCelEnv>> cel_class(m, "Env");
4345
m.def(
4446
"NewEnv",
45-
[](py::object descriptor_pool,
47+
[](py::object descriptor_pool, std::optional<PyCelEnvConfig> config,
4648
std::optional<std::unordered_map<std::string, PyCelType>> variables,
4749
std::optional<std::vector<py::object>> extensions,
4850
const std::optional<std::string>& container) {
@@ -70,15 +72,17 @@ void PyCelEnv::DefinePythonBindings(pybind11::module& m) {
7072
}
7173
}
7274

73-
return PyCelEnv(pool_ptr,
75+
return PyCelEnv(config.value_or(PyCelEnvConfig()), pool_ptr,
7476
std::move(variables).value_or(
7577
std::unordered_map<std::string, PyCelType>{}),
7678
ext_ptrs, container.value_or(""));
7779
},
78-
py::arg("descriptor_pool") = py::none(),
80+
py::arg("descriptor_pool") = py::none(), py::arg("config") = py::none(),
7981
py::arg("variables") = py::none(), py::arg("extensions") = py::none(),
8082
py::arg("container") = py::none());
8183
cel_class
84+
.def("config",
85+
[](PyCelEnv& self) { return self.GetEnv()->GetEnvConfig(); })
8286
.def("compile", &PyCelEnv::Compile, py::arg("expression"),
8387
py::arg("disable_check") = false)
8488
.def("deserialize", &PyCelEnv::Deserialize, py::arg("serialized"))
@@ -108,13 +112,13 @@ void PyCelEnv::DefinePythonBindings(pybind11::module& m) {
108112
py::arg("arena") = nullptr);
109113
}
110114

111-
PyCelEnv::PyCelEnv(PyObject* descriptor_pool,
115+
PyCelEnv::PyCelEnv(const PyCelEnvConfig& config, PyObject* descriptor_pool,
112116
std::unordered_map<std::string, PyCelType> variable_types,
113117
const std::vector<PyObject*>& extensions,
114-
std::string container)
115-
: env_(std::make_unique<PyCelEnvInternal>(
116-
descriptor_pool, std::move(variable_types), extensions,
117-
std::move(container))) {
118+
std::string container) {
119+
env_ = ThrowIfError(PyCelEnvInternal::NewCelEnvInternal(
120+
config, descriptor_pool, std::move(variable_types), extensions,
121+
std::move(container)));
118122
ABSL_CHECK(PyGILState_Check());
119123
}
120124

cel_expr_python/py_cel_env.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
#include "cel_expr_python/py_cel_activation.h"
2727
#include "cel_expr_python/py_cel_arena.h"
28+
#include "cel_expr_python/py_cel_env_config.h"
2829
#include "cel_expr_python/py_cel_expression.h"
2930
#include "cel_expr_python/py_cel_function.h"
3031
#include "cel_expr_python/py_cel_type.h"
@@ -65,9 +66,10 @@ class PyCelEnv {
6566

6667
private:
6768
// Private constructor. Use `py_cel.NewEnv()` in python to obtain an instance.
68-
PyCelEnv(PyObject* descriptor_pool,
69+
PyCelEnv(const PyCelEnvConfig& config, PyObject* descriptor_pool,
6970
std::unordered_map<std::string, PyCelType> variable_types,
7071
const std::vector<PyObject*>& extensions, std::string container);
72+
7173
std::shared_ptr<PyCelEnvInternal> env_;
7274
};
7375

cel_expr_python/py_cel_env_config.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,14 @@ class PyCelEnvConfig {
2727
public:
2828
static void DefinePythonBindings(pybind11::module& m);
2929

30+
PyCelEnvConfig() = default;
31+
explicit PyCelEnvConfig(const cel::Config& config) : config_(config) {}
32+
3033
static PyCelEnvConfig FromYaml(std::string yaml);
3134
std::string ToYaml() const;
3235

36+
const cel::Config& GetConfig() const { return config_; }
37+
3338
private:
3439
cel::Config config_;
3540
};

0 commit comments

Comments
 (0)