Skip to content

Commit 0b56b47

Browse files
dmitriplotnikovcopybara-github
authored andcommitted
Support passing cel.ExpressionContainer to cel.NewEnv to allow specifying aliases and abbreviations for namespace resolution.
PiperOrigin-RevId: 912692728
1 parent 810cbc7 commit 0b56b47

7 files changed

Lines changed: 121 additions & 20 deletions

File tree

cel_expr_python/BUILD

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,10 @@ pybind_extension(
6666
"@com_google_absl//absl/types:optional",
6767
"@com_google_absl//absl/types:span",
6868
"@com_google_cel_cpp//checker:type_checker_builder",
69-
"@com_google_cel_cpp//checker:type_checker_builder_factory",
7069
"@com_google_cel_cpp//checker:validation_result",
7170
"@com_google_cel_cpp//common:ast",
7271
"@com_google_cel_cpp//common:ast_proto",
72+
"@com_google_cel_cpp//common:container",
7373
"@com_google_cel_cpp//common:decl",
7474
"@com_google_cel_cpp//common:function_descriptor",
7575
"@com_google_cel_cpp//common:kind",
@@ -87,8 +87,6 @@ pybind_extension(
8787
"@com_google_cel_cpp//env:env_yaml",
8888
"@com_google_cel_cpp//env:runtime_std_extensions",
8989
"@com_google_cel_cpp//extensions/protobuf:runtime_adapter",
90-
"@com_google_cel_cpp//parser",
91-
"@com_google_cel_cpp//parser:options",
9290
"@com_google_cel_cpp//parser:parser_interface",
9391
"@com_google_cel_cpp//runtime",
9492
"@com_google_cel_cpp//runtime:activation",

cel_expr_python/cel.pyi

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ class CelExtensionBase:
1515
class EnvConfig:
1616
def to_yaml(self) -> str: ...
1717

18+
class ExpressionContainer:
19+
def __init__(self, name: str = ..., abbreviations: Sequence[str] | None = ..., aliases: Mapping[str, str] | None = ...) -> None: ...
20+
def container(self) -> str: ...
21+
1822
class Env:
1923
def Activation(self, data: Mapping[str, Any] | None = ..., functions: Sequence[Function] | None = ..., arena: _InternalArena = ...) -> Activation: ...
2024
def compile(self, expression: str, disable_check: bool = ...) -> Expression: ...
@@ -80,6 +84,6 @@ class _InternalArena:
8084

8185
def Arena() -> _InternalArena: ...
8286

83-
def NewEnv(descriptor_pool: proto_descriptor_pool.DescriptorPool | Any | None = ..., config: EnvConfig | None = ..., variables: Mapping[str, Type] | None = ..., extensions: Sequence[CelExtensionBase] | None = ..., container: str | None = ..., functions: Sequence[FunctionDecl] | None = ..., function_impls: Mapping[str, Callable[..., Any]] | None = ...) -> Env: ...
87+
def NewEnv(descriptor_pool: proto_descriptor_pool.DescriptorPool | Any | None = ..., config: EnvConfig | None = ..., variables: Mapping[str, Type] | None = ..., extensions: Sequence[CelExtensionBase] | None = ..., container: str | ExpressionContainer | None = ..., functions: Sequence[FunctionDecl] | None = ..., function_impls: Mapping[str, Callable[..., Any]] | None = ...) -> Env: ...
8488

8589
def NewEnvConfigFromYaml(yaml: str) -> EnvConfig: ...

cel_expr_python/cel_env_test.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,45 @@ def test_config_export_container(self):
108108
"""),
109109
)
110110

111+
def test_expression_container_abbreviation(self):
112+
expr_container = cel.ExpressionContainer(
113+
"test.container", abbreviations=["x.y.foo"]
114+
)
115+
116+
env: cel.Env = cel.NewEnv(
117+
container=expr_container,
118+
variables={
119+
"x.y.foo": cel.Type.INT,
120+
},
121+
)
122+
123+
res = env.compile("foo").eval(data={"x.y.foo": 42})
124+
self.assertEqual(res.value(), 42)
125+
yaml: str = env.config().to_yaml()
126+
self.assertEqual(
127+
normalize_yaml(yaml),
128+
normalize_yaml("""
129+
container:
130+
abbreviations:
131+
- "x.y.foo"
132+
"""),
133+
)
134+
135+
def test_expression_container_alias(self):
136+
expr_container = cel.ExpressionContainer(
137+
"test.container", aliases={"abc": "x.y.bar"}
138+
)
139+
140+
env: cel.Env = cel.NewEnv(
141+
container=expr_container,
142+
variables={
143+
"x.y.bar": cel.Type.INT,
144+
},
145+
)
146+
147+
res = env.compile("abc").eval(data={"x.y.bar": 42})
148+
self.assertEqual(res.value(), 42)
149+
111150
def test_config_export_variables(self):
112151
config: cel.Env = cel.NewEnv(
113152
variables={

cel_expr_python/py_cel_env.cc

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <vector>
2626

2727
#include "absl/log/absl_check.h"
28+
#include "common/container.h"
2829
#include "env/config.h"
2930
#include "cel_expr_python/py_cel_activation.h"
3031
#include "cel_expr_python/py_cel_arena.h"
@@ -42,13 +43,48 @@ namespace cel_python {
4243
namespace py = ::pybind11;
4344

4445
void PyCelEnv::DefinePythonBindings(pybind11::module& m) {
46+
py::class_<cel::ExpressionContainer>(m, "ExpressionContainer")
47+
.def(py::init(
48+
[](const std::string& name,
49+
const std::optional<std::vector<std::string>>& abbreviations,
50+
const std::optional<
51+
std::unordered_map<std::string, std::string>>& aliases) {
52+
auto container =
53+
ThrowIfError(cel::MakeExpressionContainer(name));
54+
55+
if (abbreviations) {
56+
for (const auto& abrev : *abbreviations) {
57+
auto status = container.AddAbbreviation(abrev);
58+
if (!status.ok()) {
59+
throw StatusToException(status);
60+
}
61+
}
62+
}
63+
64+
if (aliases) {
65+
for (const auto& [alias, full_name] : *aliases) {
66+
auto status = container.AddAlias(alias, full_name);
67+
if (!status.ok()) {
68+
throw StatusToException(status);
69+
}
70+
}
71+
}
72+
73+
return container;
74+
}),
75+
py::arg("name") = "", py::arg("abbreviations") = py::none(),
76+
py::arg("aliases") = py::none())
77+
.def("container", [](const cel::ExpressionContainer& self) {
78+
return std::string(self.container());
79+
});
80+
4581
py::class_<PyCelEnv, std::shared_ptr<PyCelEnv>> cel_class(m, "Env");
4682
m.def(
4783
"NewEnv",
4884
[](py::object descriptor_pool, std::optional<PyCelEnvConfig>& config,
4985
std::optional<std::unordered_map<std::string, PyCelType>>& variables,
5086
std::optional<std::vector<py::object>>& extensions,
51-
const std::optional<std::string>& container,
87+
py::object container,
5288
std::optional<std::vector<std::shared_ptr<PyCelFunctionDecl>>>&
5389
functions,
5490
std::optional<std::unordered_map<std::string, py::object>>&
@@ -77,10 +113,23 @@ void PyCelEnv::DefinePythonBindings(pybind11::module& m) {
77113
}
78114
}
79115

116+
cel::ExpressionContainer expr_container;
117+
if (!container.is_none()) {
118+
if (py::isinstance<py::str>(container)) {
119+
expr_container = ThrowIfError(
120+
cel::MakeExpressionContainer(container.cast<std::string>()));
121+
} else if (py::isinstance<cel::ExpressionContainer>(container)) {
122+
expr_container = container.cast<cel::ExpressionContainer>();
123+
} else {
124+
throw py::type_error(
125+
"container must be a string or ExpressionContainer");
126+
}
127+
}
128+
80129
return PyCelEnv(config.value_or(PyCelEnvConfig()), pool_ptr,
81130
std::move(variables).value_or(
82131
std::unordered_map<std::string, PyCelType>{}),
83-
ext_ptrs, container.value_or(""),
132+
ext_ptrs, std::move(expr_container),
84133
functions.value_or(
85134
std::vector<std::shared_ptr<PyCelFunctionDecl>>{}),
86135
function_impls.value_or(
@@ -125,7 +174,8 @@ void PyCelEnv::DefinePythonBindings(pybind11::module& m) {
125174
PyCelEnv::PyCelEnv(
126175
const PyCelEnvConfig& config, PyObject* descriptor_pool,
127176
const std::unordered_map<std::string, PyCelType>& variable_types,
128-
const std::vector<PyObject*>& extensions, const std::string& container,
177+
const std::vector<PyObject*>& extensions,
178+
cel::ExpressionContainer container,
129179
const std::vector<std::shared_ptr<PyCelFunctionDecl>>& functions,
130180
const std::unordered_map<std::string, py::object>& function_impls) {
131181
env_ = ThrowIfError(PyCelEnvInternal::NewCelEnvInternal(

cel_expr_python/py_cel_env.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <unordered_map>
2424
#include <vector>
2525

26+
#include "common/container.h"
2627
#include "cel_expr_python/py_cel_activation.h"
2728
#include "cel_expr_python/py_cel_arena.h"
2829
#include "cel_expr_python/py_cel_env_config.h"
@@ -70,7 +71,7 @@ class PyCelEnv {
7071
PyCelEnv(const PyCelEnvConfig& config, PyObject* descriptor_pool,
7172
const std::unordered_map<std::string, PyCelType>& variable_types,
7273
const std::vector<PyObject*>& extensions,
73-
const std::string& container,
74+
cel::ExpressionContainer container,
7475
const std::vector<std::shared_ptr<PyCelFunctionDecl>>& functions,
7576
const std::unordered_map<std::string, py::object>& function_impls);
7677

cel_expr_python/py_cel_env_internal.cc

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "absl/status/statusor.h"
2727
#include "absl/strings/str_cat.h"
2828
#include "checker/type_checker_builder.h"
29+
#include "common/container.h"
2930
#include "common/function_descriptor.h"
3031
#include "common/kind.h"
3132
#include "common/type.h"
@@ -67,8 +68,10 @@ static const cel::FunctionDescriptorOptions kFunctionDescriptorOptions = {
6768
PyCelEnvInternal::PyCelEnvInternal(
6869
const PyCelEnvConfig& env_config, PyObject* py_descriptor_pool,
6970
std::vector<CelExtensionHandle> extension_handles,
70-
absl::flat_hash_map<std::string, py::object>& function_impls)
71+
absl::flat_hash_map<std::string, py::object>& function_impls,
72+
cel::ExpressionContainer container)
7173
: env_config_(env_config),
74+
container_(std::move(container)),
7275
py_descriptor_database_(py_descriptor_pool),
7376
descriptor_pool_(
7477
std::make_shared<google::protobuf::DescriptorPool>(&py_descriptor_database_)),
@@ -106,7 +109,8 @@ absl::StatusOr<std::shared_ptr<PyCelEnvInternal>>
106109
PyCelEnvInternal::NewCelEnvInternal(
107110
const PyCelEnvConfig& env_config, PyObject* py_descriptor_pool,
108111
const std::unordered_map<std::string, PyCelType>& variable_types,
109-
const std::vector<PyObject*>& extensions, const std::string& container,
112+
const std::vector<PyObject*>& extensions,
113+
cel::ExpressionContainer container,
110114
const std::vector<std::shared_ptr<PyCelFunctionDecl>>& functions,
111115
const std::unordered_map<std::string, py::object>& function_impls) {
112116
cel::Config config = env_config.GetConfig();
@@ -151,7 +155,8 @@ PyCelEnvInternal::NewCelEnvInternal(
151155
extension_handles.push_back(std::move(handle));
152156
}
153157

154-
config.SetContainerConfig(cel::Config::ContainerConfig{.name = container});
158+
config.SetContainerConfig(
159+
cel::Config::ContainerConfig{.name = std::string(container.container())});
155160

156161
absl::flat_hash_map<std::string, py::object> impls;
157162
for (const std::shared_ptr<PyCelFunctionDecl>& function : functions) {
@@ -178,9 +183,9 @@ PyCelEnvInternal::NewCelEnvInternal(
178183
overload_id, "' already exists."));
179184
}
180185
}
181-
return std::shared_ptr<PyCelEnvInternal>(
182-
new PyCelEnvInternal(PyCelEnvConfig(config), py_descriptor_pool,
183-
std::move(extension_handles), impls));
186+
return std::shared_ptr<PyCelEnvInternal>(new PyCelEnvInternal(
187+
PyCelEnvConfig(config), py_descriptor_pool, std::move(extension_handles),
188+
impls, std::move(container)));
184189
}
185190

186191
absl::StatusOr<const cel::Compiler*> PyCelEnvInternal::GetCompiler(
@@ -200,7 +205,7 @@ absl::StatusOr<const cel::Compiler*> PyCelEnvInternal::GetCompiler(
200205
cel::TypeCheckerBuilder& checker_builder =
201206
compiler_builder->GetCheckerBuilder();
202207

203-
checker_builder.set_container(config.GetContainerConfig().name);
208+
checker_builder.SetExpressionContainer(env->container_);
204209

205210
// Convert variable types from cel::TypeInfo to PyCelType.
206211
google::protobuf::Arena* arena = checker_builder.arena();

cel_expr_python/py_cel_env_internal.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "absl/container/flat_hash_map.h"
2727
#include "absl/status/status.h"
2828
#include "absl/status/statusor.h"
29+
#include "common/container.h"
2930
#include "common/function_descriptor.h"
3031
#include "compiler/compiler.h"
3132
#include "env/env.h"
@@ -76,7 +77,8 @@ class PyCelEnvInternal {
7677
static absl::StatusOr<std::shared_ptr<PyCelEnvInternal>> NewCelEnvInternal(
7778
const PyCelEnvConfig& env_config, PyObject* py_descriptor_pool,
7879
const std::unordered_map<std::string, PyCelType>& variable_types,
79-
const std::vector<PyObject*>& extensions, const std::string& container,
80+
const std::vector<PyObject*>& extensions,
81+
cel::ExpressionContainer container,
8082
const std::vector<std::shared_ptr<PyCelFunctionDecl>>& functions,
8183
const std::unordered_map<std::string, py::object>& function_impls);
8284

@@ -112,10 +114,11 @@ class PyCelEnvInternal {
112114

113115
private:
114116
// Use NewCelEnvInternal() to create an instance.
115-
PyCelEnvInternal(
116-
const PyCelEnvConfig& env_config, PyObject* py_descriptor_pool,
117-
std::vector<CelExtensionHandle> extensions,
118-
absl::flat_hash_map<std::string, py::object>& function_impls);
117+
PyCelEnvInternal(const PyCelEnvConfig& env_config,
118+
PyObject* py_descriptor_pool,
119+
std::vector<CelExtensionHandle> extensions,
120+
absl::flat_hash_map<std::string, py::object>& function_impls,
121+
cel::ExpressionContainer container);
119122

120123
absl::Status ConfigureStandardExtension(
121124
cel::CompilerBuilder& compiler_builder, const std::string& extension);
@@ -128,6 +131,7 @@ class PyCelEnvInternal {
128131
cel::Env cel_env_;
129132
cel::EnvRuntime cel_env_runtime_;
130133
PyCelEnvConfig env_config_;
134+
cel::ExpressionContainer container_;
131135
PyDescriptorDatabase py_descriptor_database_;
132136
std::shared_ptr<google::protobuf::DescriptorPool> descriptor_pool_;
133137
google::protobuf::DynamicMessageFactory message_factory_;

0 commit comments

Comments
 (0)