Skip to content

Commit 07a04e7

Browse files
dmitriplotnikovcopybara-github
authored andcommitted
Add extension version and alias support
PiperOrigin-RevId: 896174560
1 parent 35a65f6 commit 07a04e7

12 files changed

Lines changed: 191 additions & 32 deletions

File tree

cel_expr_python/BUILD

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,14 +105,25 @@ pybind_extension(
105105
# For pybind11-based CEL extensions.
106106
pybind_library(
107107
name = "cel_extension",
108-
hdrs = ["cel_extension.h"],
108+
srcs = [
109+
"py_error_status.cc",
110+
],
111+
hdrs = [
112+
"cel_extension.h",
113+
"py_error_status.h",
114+
],
109115
visibility = ["//visibility:public"],
110116
deps = [
117+
":status_macros",
118+
"@com_google_absl//absl/base:no_destructor",
119+
"@com_google_absl//absl/container:flat_hash_map",
120+
"@com_google_absl//absl/log:absl_check",
121+
"@com_google_absl//absl/log:absl_log",
111122
"@com_google_absl//absl/status",
123+
"@com_google_absl//absl/status:statusor",
112124
"@com_google_cel_cpp//compiler",
113125
"@com_google_cel_cpp//runtime:runtime_builder",
114126
"@com_google_cel_cpp//runtime:runtime_options",
115-
"@com_google_protobuf//:protobuf",
116127
],
117128
)
118129

@@ -141,7 +152,10 @@ py_test(
141152
srcs = ["cel_env_test.py"],
142153
deps = [
143154
":cel",
155+
"//cel_expr_python/ext:ext_bindings",
144156
"//cel_expr_python/ext:ext_math",
157+
"//cel_expr_python/ext:ext_optional",
158+
"//cel_expr_python/ext:ext_strings",
145159
"//testing:proto2_test_all_types_py_pb2",
146160
"@com_google_absl_py//absl/testing:absltest",
147161
],

cel_expr_python/cel_env_test.py

Lines changed: 79 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@
2222

2323
from absl.testing import absltest
2424
from cel_expr_python import cel
25+
from cel_expr_python.ext import ext_bindings
2526
from cel_expr_python.ext import ext_math
27+
from cel_expr_python.ext import ext_optional
28+
from cel_expr_python.ext import ext_strings
2629
from cel.expr.conformance.proto2 import test_all_types_pb2 as test_all_types_pb
2730

2831

@@ -95,9 +98,7 @@ def test_invalid_yaml(self):
9598
)
9699

97100
def test_config_export_container(self):
98-
env = cel.NewEnv(
99-
container="test.container"
100-
)
101+
env = cel.NewEnv(container="test.container")
101102
yaml = env.config().to_yaml()
102103
self.assertEqual(
103104
normalize_yaml(yaml),
@@ -251,6 +252,52 @@ def test_config_variable_types(self):
251252
self.assertEqual(res.type(), cel.Type.INT)
252253
self.assertEqual(res.value(), 42)
253254

255+
def test_config_export_extension_version(self):
256+
env = cel.NewEnv(
257+
extensions=[
258+
ext_math.ExtMath(0),
259+
ext_optional.ExtOptional(1),
260+
ext_strings.ExtStrings(2),
261+
ext_bindings.ExtBindings(),
262+
],
263+
)
264+
yaml = env.config().to_yaml()
265+
self.assertEqual(
266+
normalize_yaml(yaml),
267+
normalize_yaml("""
268+
extensions:
269+
- name: "bindings"
270+
- name: "math"
271+
version: 0
272+
- name: "optional"
273+
version: 1
274+
- name: "strings"
275+
version: 2
276+
"""),
277+
)
278+
279+
def test_config_extension_version_out_of_range(self):
280+
cases = [
281+
[
282+
lambda: ext_math.ExtMath(42),
283+
r"'math' extension version: 42 not in range \[0, \d+\]",
284+
],
285+
[
286+
lambda: ext_optional.ExtOptional(6),
287+
r"'optional' extension version: 6 not in range \[0, \d+\]",
288+
],
289+
[
290+
lambda: ext_strings.ExtStrings(18),
291+
r"'strings' extension version: 18 not in range \[0, \d+\]",
292+
],
293+
]
294+
for test_case in cases:
295+
with self.assertRaises(Exception) as e:
296+
cel.NewEnv(
297+
extensions=[test_case[0]()],
298+
)
299+
self.assertRegex(str(e.exception), test_case[1])
300+
254301
def test_config_extensions(self):
255302
config = cel.NewEnvConfigFromYaml("""
256303
extensions:
@@ -276,23 +323,47 @@ def test_config_extensions(self):
276323
res = env.compile("hello('World')").eval()
277324
self.assertEqual(res.value(), "Hello, World!")
278325

279-
def test_config_extensions_override(self):
280-
# TODO(b/498655870): add assertion based on extension aliases once
281-
# supported.
326+
def test_config_extension_override_same_version(self):
282327
config = cel.NewEnvConfigFromYaml("""
283328
extensions:
284329
- name: cel.lib.ext.math
330+
version: 1
331+
- name: strings
332+
version: 2
333+
""")
334+
env = cel.NewEnv(
335+
config=config,
336+
extensions=[ext_math.ExtMath(1), ext_strings.ExtStrings(2)],
337+
)
338+
res = env.compile("'%.3f'.format([math.floor(3.14)])").eval()
339+
self.assertEqual(res.value(), "3.000")
340+
341+
def test_config_extension_override_different_version(self):
342+
config = cel.NewEnvConfigFromYaml("""
343+
extensions:
344+
- name: math
285345
version: 0
286346
- name: cel.lib.ext.strings
347+
version: 2
287348
""")
288349
with self.assertRaises(Exception) as e:
289350
cel.NewEnv(
290351
config=config,
291352
extensions=[ext_math.ExtMath()],
292353
)
293354
self.assertIn(
294-
"Extension 'cel.lib.ext.math' version 0 is already included. Cannot"
295-
" also include version 'latest'",
355+
"Extension 'math' version 0 is already included. Cannot"
356+
" also include version 2",
357+
str(e.exception),
358+
)
359+
with self.assertRaises(Exception) as e:
360+
cel.NewEnv(
361+
config=config,
362+
extensions=[ext_strings.ExtStrings(1)],
363+
)
364+
self.assertIn(
365+
"Extension 'cel.lib.ext.strings' version 2 is already included. Cannot"
366+
" also include version 1",
296367
str(e.exception),
297368
)
298369

cel_expr_python/cel_extension.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ namespace cel_python {
3838
// Python.
3939
class CelExtension {
4040
public:
41-
explicit CelExtension(std::string name) : name_(std::move(name)) {};
41+
explicit CelExtension(std::string name, std::string alias = "",
42+
int version = -1)
43+
: name_(std::move(name)), alias_(std::move(alias)), version_(version) {}
4244
virtual ~CelExtension() = default;
4345

4446
virtual cel::CompilerLibrary GetCompilerLibrary() {
@@ -51,9 +53,13 @@ class CelExtension {
5153
}
5254

5355
std::string name() const { return name_; }
56+
std::string alias() const { return alias_; }
57+
int version() const { return version_; }
5458

5559
private:
5660
std::string name_;
61+
std::string alias_;
62+
int version_;
5763
};
5864

5965
#define CEL_MODULE_NAME "cel_expr_python.cel"
@@ -80,6 +86,13 @@ class CelExtension {
8086
.def(pybind11::init<>()); \
8187
}
8288

89+
#define CEL_VERSIONED_EXTENSION_MODULE(module_name, class_name) \
90+
PYBIND11_MODULE(module_name, m) { \
91+
pybind11::module_::import(CEL_MODULE_NAME); \
92+
pybind11::class_<class_name, cel_python::CelExtension>(m, #class_name) \
93+
.def(pybind11::init<>()) \
94+
.def(pybind11::init<int>(), pybind11::arg("version")); \
95+
}
8396
} // namespace cel_python
8497

8598
#endif // THIRD_PARTY_CEL_PYTHON_CEL_EXTENSION_H_

cel_expr_python/ext/BUILD

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ pybind_extension(
4949
deps = [
5050
"//cel_expr_python:cel_extension",
5151
"@com_google_absl//absl/status",
52+
"@com_google_absl//absl/strings",
5253
"@com_google_cel_cpp//compiler",
5354
"@com_google_cel_cpp//extensions:math_ext",
5455
"@com_google_cel_cpp//extensions:math_ext_decls",
@@ -69,6 +70,8 @@ pybind_extension(
6970
deps = [
7071
"//cel_expr_python:cel_extension",
7172
"@com_google_absl//absl/status",
73+
"@com_google_absl//absl/strings",
74+
"@com_google_cel_cpp//checker:optional",
7275
"@com_google_cel_cpp//compiler",
7376
"@com_google_cel_cpp//compiler:optional",
7477
"@com_google_cel_cpp//runtime:optional_types",
@@ -94,9 +97,9 @@ pybind_extension(
9497
)
9598

9699
pybind_extension(
97-
name = "ext_string",
100+
name = "ext_strings",
98101
srcs = [
99-
"ext_string.cc",
102+
"ext_strings.cc",
100103
],
101104
data = [
102105
"//cel_expr_python:cel",
@@ -105,6 +108,7 @@ pybind_extension(
105108
deps = [
106109
"//cel_expr_python:cel_extension",
107110
"@com_google_absl//absl/status",
111+
"@com_google_absl//absl/strings",
108112
"@com_google_cel_cpp//compiler",
109113
"@com_google_cel_cpp//extensions:strings",
110114
"@com_google_cel_cpp//runtime:runtime_builder",

cel_expr_python/ext/ext_bindings.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ namespace cel_python {
2020

2121
class ExtBindings : public CelExtension {
2222
public:
23-
explicit ExtBindings() : CelExtension("cel.lib.ext.cel.bindings") {}
23+
explicit ExtBindings()
24+
: CelExtension("cel.lib.ext.cel.bindings", "bindings") {}
2425

2526
cel::CompilerLibrary GetCompilerLibrary() override {
2627
return cel::extensions::BindingsCompilerLibrary();

cel_expr_python/ext/ext_encoders.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ namespace cel_python {
2323

2424
class ExtEncoders : public CelExtension {
2525
public:
26-
explicit ExtEncoders() : CelExtension("cel.lib.ext.encoders") {}
26+
explicit ExtEncoders() : CelExtension("cel.lib.ext.encoders", "encoders") {}
2727

2828
cel::CompilerLibrary GetCompilerLibrary() override {
2929
return cel::extensions::EncodersCompilerLibrary();

cel_expr_python/ext/ext_math.cc

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,30 +13,41 @@
1313
// limitations under the License.
1414

1515
#include "absl/status/status.h"
16+
#include "absl/strings/str_cat.h"
1617
#include "compiler/compiler.h"
1718
#include "extensions/math_ext.h"
1819
#include "extensions/math_ext_decls.h"
1920
#include "runtime/runtime_builder.h"
2021
#include "runtime/runtime_options.h"
2122
#include "cel_expr_python/cel_extension.h"
23+
#include "cel_expr_python/py_error_status.h"
2224

2325
namespace cel_python {
2426

2527
class ExtMath : public CelExtension {
2628
public:
27-
explicit ExtMath() : CelExtension("cel.lib.ext.math") {}
29+
explicit ExtMath(int version)
30+
: CelExtension("cel.lib.ext.math", "math", version) {
31+
if (version < 0 || version > cel::extensions::kMathExtensionLatestVersion) {
32+
throw StatusToException(absl::InvalidArgumentError(absl::StrCat(
33+
"'math' extension version: ", version, " not in range [0, ",
34+
cel::extensions::kMathExtensionLatestVersion, "]")));
35+
}
36+
}
37+
38+
ExtMath() : ExtMath(cel::extensions::kMathExtensionLatestVersion) {}
2839

2940
cel::CompilerLibrary GetCompilerLibrary() override {
30-
return cel::extensions::MathCompilerLibrary();
41+
return cel::extensions::MathCompilerLibrary(version());
3142
}
3243

3344
absl::Status ConfigureRuntime(cel::RuntimeBuilder& runtime_builder,
3445
const cel::RuntimeOptions& opts) override {
3546
return cel::extensions::RegisterMathExtensionFunctions(
36-
runtime_builder.function_registry(), opts);
47+
runtime_builder.function_registry(), opts, version());
3748
}
3849
};
3950

40-
CEL_EXTENSION_MODULE(ext_math, ExtMath);
51+
CEL_VERSIONED_EXTENSION_MODULE(ext_math, ExtMath);
4152

4253
} // namespace cel_python

cel_expr_python/ext/ext_optional.cc

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,32 @@
1313
// limitations under the License.
1414

1515
#include "absl/status/status.h"
16+
#include "absl/strings/str_cat.h"
17+
#include "checker/optional.h"
1618
#include "compiler/compiler.h"
1719
#include "compiler/optional.h"
1820
#include "runtime/optional_types.h"
1921
#include "runtime/runtime_builder.h"
2022
#include "runtime/runtime_options.h"
2123
#include "cel_expr_python/cel_extension.h"
24+
#include "cel_expr_python/py_error_status.h"
2225

2326
namespace cel_python {
2427

2528
class ExtOptional : public CelExtension {
2629
public:
27-
explicit ExtOptional() : CelExtension("optional") {}
30+
explicit ExtOptional(int version) : CelExtension("optional", "", version) {
31+
if (version < 0 || version > cel::kOptionalExtensionLatestVersion) {
32+
throw StatusToException(absl::InvalidArgumentError(absl::StrCat(
33+
"'optional' extension version: ", version, " not in range [0, ",
34+
cel::kOptionalExtensionLatestVersion, "]")));
35+
}
36+
}
37+
38+
ExtOptional() : ExtOptional(cel::kOptionalExtensionLatestVersion) {}
2839

2940
cel::CompilerLibrary GetCompilerLibrary() override {
30-
return cel::OptionalCompilerLibrary();
41+
return cel::OptionalCompilerLibrary(version());
3142
}
3243

3344
absl::Status ConfigureRuntime(cel::RuntimeBuilder& runtime_builder,
@@ -40,6 +51,6 @@ class ExtOptional : public CelExtension {
4051
}
4152
};
4253

43-
CEL_EXTENSION_MODULE(ext_optional, ExtOptional);
54+
CEL_VERSIONED_EXTENSION_MODULE(ext_optional, ExtOptional);
4455

4556
} // namespace cel_python
Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,29 +13,42 @@
1313
// limitations under the License.
1414

1515
#include "absl/status/status.h"
16+
#include "absl/strings/str_cat.h"
1617
#include "compiler/compiler.h"
1718
#include "extensions/strings.h"
1819
#include "runtime/runtime_builder.h"
1920
#include "runtime/runtime_options.h"
2021
#include "cel_expr_python/cel_extension.h"
22+
#include "cel_expr_python/py_error_status.h"
2123

2224
namespace cel_python {
2325

24-
class ExtString : public CelExtension {
26+
class ExtStrings : public CelExtension {
2527
public:
26-
explicit ExtString() : CelExtension("cel.lib.ext.string") {}
28+
explicit ExtStrings(int version)
29+
: CelExtension("cel.lib.ext.strings", "strings", version) {
30+
if (version < 0 ||
31+
version > cel::extensions::kStringsExtensionLatestVersion) {
32+
throw StatusToException(absl::InvalidArgumentError(absl::StrCat(
33+
"'strings' extension version: ", version, " not in range [0, ",
34+
cel::extensions::kStringsExtensionLatestVersion, "]")));
35+
}
36+
}
37+
38+
ExtStrings() : ExtStrings(cel::extensions::kStringsExtensionLatestVersion) {}
2739

2840
cel::CompilerLibrary GetCompilerLibrary() override {
29-
return cel::extensions::StringsCompilerLibrary();
41+
return cel::extensions::StringsCompilerLibrary(version());
3042
}
3143

3244
absl::Status ConfigureRuntime(cel::RuntimeBuilder& runtime_builder,
3345
const cel::RuntimeOptions& opts) override {
3446
return cel::extensions::RegisterStringsFunctions(
35-
runtime_builder.function_registry(), opts);
47+
runtime_builder.function_registry(), opts,
48+
cel::extensions::StringsExtensionOptions{.version = version()});
3649
}
3750
};
3851

39-
CEL_EXTENSION_MODULE(ext_string, ExtString);
52+
CEL_VERSIONED_EXTENSION_MODULE(ext_strings, ExtStrings);
4053

4154
} // namespace cel_python

0 commit comments

Comments
 (0)