Skip to content

Repository files navigation

cel-python

PyPI: cel-pythonGitHub Actions Build StatusApache License

Pure Python implementation of Google Common Expression Language, https://opensource.google/projects/cel.

The Common Expression Language (CEL) implements common semantics for expression evaluation, enabling different applications to more easily interoperate.

Key Applications

Security policy: organization have complex infrastructure and need common tooling to reason about the system as a whole

Protocols: expressions are a useful data type and require interoperability across programming languages and platforms.

This implementation has minimal dependencies, runs quickly, and can be embedded into Python-based applications. Specifically, the intent is to be part of Cloud Custodian, C7N, as part of the security policy filter.

Installation

python -m pip install cel-python

You now have the CEL run-time available to Python-based applications.

re2

CEL specifies that regular expressions use re2 syntax, https://github.com/google/re2/wiki/Syntax. As of the 0.4.0 release, the Google-RE2 module is part of the CEL distribution.

Command Line

We can read JSON directly from stdin, making this a bit like jq.

% python -m celpy '.this.from.json * 3 + 3' <<EOF
heredoc> {"this": {"from": {"json": 13}}}
heredoc> EOF
42

It's also a desk calculator, like expr, but with float values:

% python -m celpy -n '355.0 / 113.0'
3.1415929203539825

It's not as sophistcated as bc. But, yes, this has a tiny advantage over python -c '355/113'. Most notably, the ability to embed Google CEL into other contexts where you don't really want Python's power.

It's also capable of decision-making, like test:

% echo '{"status": 3}' | python -m celpy -sb '.status == 0'
false
% echo $?
1

We can provide a -a option to define objects with specific data types. This is particularly helpful for providing protobuf message definitions.

python -m celpy -n --arg x:int=6 --arg y:int=7 'x*y'
42

If you want to see details of evaluation, use -v.

python -m celpy -v -n '[2, 4, 6].map(n, n/2)'
... a lot of output
[1, 2, 3]

Library

To follow the pattern defined in the Go implementation, there's a multi-step process for compiling a CEL expression to create a runnable "program". This program can then be applied to argument values.

>>> import celpy
>>> cel_source = """
... account.balance >= transaction.withdrawal
... || (account.overdraftProtection
... && account.overdraftLimit >= transaction.withdrawal - account.balance)
... """
>>> env = celpy.Environment()
>>> ast = env.compile(cel_source)
>>> prgm = env.program(ast)
>>> context = {
... "account": celpy.json_to_cel({"balance": 500, "overdraftProtection": False}),
... "transaction": celpy.json_to_cel({"withdrawal": 600})
... }
>>> result = prgm.evaluate(context)
>>> result
BoolType(False)

The Python classes are generally based on the object model in https://github.com/google/cel-go These types semantics are slightly different from Python's native semantics. Type coercion is not generally done. Python // truncates toward negative infinity. Go (and CEL) / truncates toward zero.

Development

The parser is based on the grammars used by Go and C++, but processed through Python Lark.

See https://github.com/google/cel-spec/blob/master/doc/langdef.md

https://github.com/google/cel-cpp/blob/master/parser/Cel.g4

https://github.com/google/cel-go/blob/master/parser/gen/CEL.g4

The documentation includes PlantUML diagrams. The Sphinx conf.py provides the location for the PlantUML local JAR file if one is used. Currently, it expects docs/plantuml-asl-1.2025.3.jar. The JAR is not provided in this repository, get one from https://plantuml.com. If you install a different version, update the conf.py to refer to the JAR file you've downloaded.

Notes

CEL provides a number of runtime errors that are mapped to Python exceptions.

  • no_matching_overload: this function has no overload for the types of the arguments.
  • no_such_field: a map or message does not contain the desired field.
  • return error for overflow: integer arithmetic overflows

There are mapped to Python celpy.evaluation.EvalError exception. The args will have a message similar to the CEL error message, as well as an underlying Python exception.

In principle CEL can pre-check types. However, see https://github.com/google/cel-spec/blob/master/doc/langdef.md#gradual-type-checking. Rather than try to pre-check types, we'll rely on Python's implementation.

Example 2

Here's an example with some details:

>>> import celpy
# A list of type names and class bindings used to create an environment.
>>> types = []
>>> env = celpy.Environment(types)
# Parse the code to create the CEL AST.
>>> ast = env.compile("355. / 113.")
# Use the AST and any overriding functions to create an executable program.
>>> functions = {}
>>> prgm = env.program(ast, functions)
# Variable bindings.
>>> activation = {}
# Final evaluation.
>>> try:
... result = prgm.evaluate(activation)
... error = None
... except CELEvalError as ex:
... result = None
... error = ex.args[0]
>>> result # doctest: +ELLIPSIS
DoubleType(3.14159...)

Example 3

See https://github.com/google/cel-go/blob/master/examples/simple_test.go

The model Go we're sticking close to:

d := cel.Declarations(decls.NewVar("name", decls.String))
env, err := cel.NewEnv(d)
if err != nil {
log.Fatalf("environment creation error: %v\\n", err)
}
ast, iss := env.Compile(`"Hello world! I'm " + name + "."`)
// Check iss for compilation errors.
if iss.Err() != nil {
log.Fatalln(iss.Err())
}
prg, err := env.Program(ast)
if err != nil {
log.Fatalln(err)
}
out, _, err := prg.Eval(map[string]interface{}{
"name": "CEL",
})
if err != nil {
log.Fatalln(err)
}
fmt.Println(out)
// Output:Hello world! I'm CEL.

Here's the Pythonic approach, using concept patterned after the Go implementation:

>>> from celpy import *
>>> decls = {"name": celtypes.StringType}
>>> env = Environment(annotations=decls)
>>> ast = env.compile('"Hello world! I\'m " + name + "."')
>>> out = env.program(ast).evaluate({"name": "CEL"})
>>> print(out)
Hello world! I'm CEL.

Contributing

See https://cloudcustodian.io/docs/contribute.html

Code of Conduct

This project adheres to the Open Code of Conduct. By participating, you are expected to honor this code.

About

Pure Python implementation of the Common Expression Language

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
GitHub - benja0rtzzz/cel-python: Pure Python implementation of the Common Expression Language · GitHub
Skip to content

Repository files navigation

cel-python

PyPI: cel-pythonGitHub Actions Build StatusApache License

Pure Python implementation of Google Common Expression Language, https://opensource.google/projects/cel.

The Common Expression Language (CEL) implements common semantics for expression evaluation, enabling different applications to more easily interoperate.

Key Applications

Security policy: organization have complex infrastructure and need common tooling to reason about the system as a whole

Protocols: expressions are a useful data type and require interoperability across programming languages and platforms.

This implementation has minimal dependencies, runs quickly, and can be embedded into Python-based applications. Specifically, the intent is to be part of Cloud Custodian, C7N, as part of the security policy filter.

Installation

python -m pip install cel-python

You now have the CEL run-time available to Python-based applications.

re2

CEL specifies that regular expressions use re2 syntax, https://github.com/google/re2/wiki/Syntax. As of the 0.4.0 release, the Google-RE2 module is part of the CEL distribution.

Command Line

We can read JSON directly from stdin, making this a bit like jq.

% python -m celpy '.this.from.json * 3 + 3' <<EOF
heredoc> {"this": {"from": {"json": 13}}}
heredoc> EOF
42

It's also a desk calculator, like expr, but with float values:

% python -m celpy -n '355.0 / 113.0'
3.1415929203539825

It's not as sophistcated as bc. But, yes, this has a tiny advantage over python -c '355/113'. Most notably, the ability to embed Google CEL into other contexts where you don't really want Python's power.

It's also capable of decision-making, like test:

% echo '{"status": 3}' | python -m celpy -sb '.status == 0'
false
% echo $?
1

We can provide a -a option to define objects with specific data types. This is particularly helpful for providing protobuf message definitions.

python -m celpy -n --arg x:int=6 --arg y:int=7 'x*y'
42

If you want to see details of evaluation, use -v.

python -m celpy -v -n '[2, 4, 6].map(n, n/2)'
... a lot of output
[1, 2, 3]

Library

To follow the pattern defined in the Go implementation, there's a multi-step process for compiling a CEL expression to create a runnable "program". This program can then be applied to argument values.

>>> import celpy
>>> cel_source = """
... account.balance >= transaction.withdrawal
... || (account.overdraftProtection
... && account.overdraftLimit >= transaction.withdrawal - account.balance)
... """
>>> env = celpy.Environment()
>>> ast = env.compile(cel_source)
>>> prgm = env.program(ast)
>>> context = {
... "account": celpy.json_to_cel({"balance": 500, "overdraftProtection": False}),
... "transaction": celpy.json_to_cel({"withdrawal": 600})
... }
>>> result = prgm.evaluate(context)
>>> result
BoolType(False)

The Python classes are generally based on the object model in https://github.com/google/cel-go These types semantics are slightly different from Python's native semantics. Type coercion is not generally done. Python // truncates toward negative infinity. Go (and CEL) / truncates toward zero.

Development

The parser is based on the grammars used by Go and C++, but processed through Python Lark.

See https://github.com/google/cel-spec/blob/master/doc/langdef.md

https://github.com/google/cel-cpp/blob/master/parser/Cel.g4

https://github.com/google/cel-go/blob/master/parser/gen/CEL.g4

The documentation includes PlantUML diagrams. The Sphinx conf.py provides the location for the PlantUML local JAR file if one is used. Currently, it expects docs/plantuml-asl-1.2025.3.jar. The JAR is not provided in this repository, get one from https://plantuml.com. If you install a different version, update the conf.py to refer to the JAR file you've downloaded.

Notes

CEL provides a number of runtime errors that are mapped to Python exceptions.

  • no_matching_overload: this function has no overload for the types of the arguments.
  • no_such_field: a map or message does not contain the desired field.
  • return error for overflow: integer arithmetic overflows

There are mapped to Python celpy.evaluation.EvalError exception. The args will have a message similar to the CEL error message, as well as an underlying Python exception.

In principle CEL can pre-check types. However, see https://github.com/google/cel-spec/blob/master/doc/langdef.md#gradual-type-checking. Rather than try to pre-check types, we'll rely on Python's implementation.

Example 2

Here's an example with some details:

>>> import celpy
# A list of type names and class bindings used to create an environment.
>>> types = []
>>> env = celpy.Environment(types)
# Parse the code to create the CEL AST.
>>> ast = env.compile("355. / 113.")
# Use the AST and any overriding functions to create an executable program.
>>> functions = {}
>>> prgm = env.program(ast, functions)
# Variable bindings.
>>> activation = {}
# Final evaluation.
>>> try:
... result = prgm.evaluate(activation)
... error = None
... except CELEvalError as ex:
... result = None
... error = ex.args[0]
>>> result # doctest: +ELLIPSIS
DoubleType(3.14159...)

Example 3

See https://github.com/google/cel-go/blob/master/examples/simple_test.go

The model Go we're sticking close to:

d := cel.Declarations(decls.NewVar("name", decls.String))
env, err := cel.NewEnv(d)
if err != nil {
log.Fatalf("environment creation error: %v\\n", err)
}
ast, iss := env.Compile(`"Hello world! I'm " + name + "."`)
// Check iss for compilation errors.
if iss.Err() != nil {
log.Fatalln(iss.Err())
}
prg, err := env.Program(ast)
if err != nil {
log.Fatalln(err)
}
out, _, err := prg.Eval(map[string]interface{}{
"name": "CEL",
})
if err != nil {
log.Fatalln(err)
}
fmt.Println(out)
// Output:Hello world! I'm CEL.

Here's the Pythonic approach, using concept patterned after the Go implementation:

>>> from celpy import *
>>> decls = {"name": celtypes.StringType}
>>> env = Environment(annotations=decls)
>>> ast = env.compile('"Hello world! I\'m " + name + "."')
>>> out = env.program(ast).evaluate({"name": "CEL"})
>>> print(out)
Hello world! I'm CEL.

Contributing

See https://cloudcustodian.io/docs/contribute.html

Code of Conduct

This project adheres to the Open Code of Conduct. By participating, you are expected to honor this code.

About

Pure Python implementation of the Common Expression Language

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - benja0rtzzz/cel-python: Pure Python implementation of the Common Expression Language · GitHub
Skip to content

Repository files navigation

cel-python

PyPI: cel-pythonGitHub Actions Build StatusApache License

Pure Python implementation of Google Common Expression Language, https://opensource.google/projects/cel.

The Common Expression Language (CEL) implements common semantics for expression evaluation, enabling different applications to more easily interoperate.

Key Applications

Security policy: organization have complex infrastructure and need common tooling to reason about the system as a whole

Protocols: expressions are a useful data type and require interoperability across programming languages and platforms.

This implementation has minimal dependencies, runs quickly, and can be embedded into Python-based applications. Specifically, the intent is to be part of Cloud Custodian, C7N, as part of the security policy filter.

Installation

python -m pip install cel-python

You now have the CEL run-time available to Python-based applications.

re2

CEL specifies that regular expressions use re2 syntax, https://github.com/google/re2/wiki/Syntax. As of the 0.4.0 release, the Google-RE2 module is part of the CEL distribution.

Command Line

We can read JSON directly from stdin, making this a bit like jq.

% python -m celpy '.this.from.json * 3 + 3' <<EOF
heredoc> {"this": {"from": {"json": 13}}}
heredoc> EOF
42

It's also a desk calculator, like expr, but with float values:

% python -m celpy -n '355.0 / 113.0'
3.1415929203539825

It's not as sophistcated as bc. But, yes, this has a tiny advantage over python -c '355/113'. Most notably, the ability to embed Google CEL into other contexts where you don't really want Python's power.

It's also capable of decision-making, like test:

% echo '{"status": 3}' | python -m celpy -sb '.status == 0'
false
% echo $?
1

We can provide a -a option to define objects with specific data types. This is particularly helpful for providing protobuf message definitions.

python -m celpy -n --arg x:int=6 --arg y:int=7 'x*y'
42

If you want to see details of evaluation, use -v.

python -m celpy -v -n '[2, 4, 6].map(n, n/2)'
... a lot of output
[1, 2, 3]

Library

To follow the pattern defined in the Go implementation, there's a multi-step process for compiling a CEL expression to create a runnable "program". This program can then be applied to argument values.

>>> import celpy
>>> cel_source = """
... account.balance >= transaction.withdrawal
... || (account.overdraftProtection
... && account.overdraftLimit >= transaction.withdrawal - account.balance)
... """
>>> env = celpy.Environment()
>>> ast = env.compile(cel_source)
>>> prgm = env.program(ast)
>>> context = {
... "account": celpy.json_to_cel({"balance": 500, "overdraftProtection": False}),
... "transaction": celpy.json_to_cel({"withdrawal": 600})
... }
>>> result = prgm.evaluate(context)
>>> result
BoolType(False)

The Python classes are generally based on the object model in https://github.com/google/cel-go These types semantics are slightly different from Python's native semantics. Type coercion is not generally done. Python // truncates toward negative infinity. Go (and CEL) / truncates toward zero.

Development

The parser is based on the grammars used by Go and C++, but processed through Python Lark.

See https://github.com/google/cel-spec/blob/master/doc/langdef.md

https://github.com/google/cel-cpp/blob/master/parser/Cel.g4

https://github.com/google/cel-go/blob/master/parser/gen/CEL.g4

The documentation includes PlantUML diagrams. The Sphinx conf.py provides the location for the PlantUML local JAR file if one is used. Currently, it expects docs/plantuml-asl-1.2025.3.jar. The JAR is not provided in this repository, get one from https://plantuml.com. If you install a different version, update the conf.py to refer to the JAR file you've downloaded.

Notes

CEL provides a number of runtime errors that are mapped to Python exceptions.

  • no_matching_overload: this function has no overload for the types of the arguments.
  • no_such_field: a map or message does not contain the desired field.
  • return error for overflow: integer arithmetic overflows

There are mapped to Python celpy.evaluation.EvalError exception. The args will have a message similar to the CEL error message, as well as an underlying Python exception.

In principle CEL can pre-check types. However, see https://github.com/google/cel-spec/blob/master/doc/langdef.md#gradual-type-checking. Rather than try to pre-check types, we'll rely on Python's implementation.

Example 2

Here's an example with some details:

>>> import celpy
# A list of type names and class bindings used to create an environment.
>>> types = []
>>> env = celpy.Environment(types)
# Parse the code to create the CEL AST.
>>> ast = env.compile("355. / 113.")
# Use the AST and any overriding functions to create an executable program.
>>> functions = {}
>>> prgm = env.program(ast, functions)
# Variable bindings.
>>> activation = {}
# Final evaluation.
>>> try:
... result = prgm.evaluate(activation)
... error = None
... except CELEvalError as ex:
... result = None
... error = ex.args[0]
>>> result # doctest: +ELLIPSIS
DoubleType(3.14159...)

Example 3

See https://github.com/google/cel-go/blob/master/examples/simple_test.go

The model Go we're sticking close to:

d := cel.Declarations(decls.NewVar("name", decls.String))
env, err := cel.NewEnv(d)
if err != nil {
log.Fatalf("environment creation error: %v\\n", err)
}
ast, iss := env.Compile(`"Hello world! I'm " + name + "."`)
// Check iss for compilation errors.
if iss.Err() != nil {
log.Fatalln(iss.Err())
}
prg, err := env.Program(ast)
if err != nil {
log.Fatalln(err)
}
out, _, err := prg.Eval(map[string]interface{}{
"name": "CEL",
})
if err != nil {
log.Fatalln(err)
}
fmt.Println(out)
// Output:Hello world! I'm CEL.

Here's the Pythonic approach, using concept patterned after the Go implementation:

>>> from celpy import *
>>> decls = {"name": celtypes.StringType}
>>> env = Environment(annotations=decls)
>>> ast = env.compile('"Hello world! I\'m " + name + "."')
>>> out = env.program(ast).evaluate({"name": "CEL"})
>>> print(out)
Hello world! I'm CEL.

Contributing

See https://cloudcustodian.io/docs/contribute.html

Code of Conduct

This project adheres to the Open Code of Conduct. By participating, you are expected to honor this code.

About

Pure Python implementation of the Common Expression Language

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - benja0rtzzz/cel-python: Pure Python implementation of the Common Expression Language · GitHub
Skip to content

Repository files navigation

cel-python

PyPI: cel-pythonGitHub Actions Build StatusApache License

Pure Python implementation of Google Common Expression Language, https://opensource.google/projects/cel.

The Common Expression Language (CEL) implements common semantics for expression evaluation, enabling different applications to more easily interoperate.

Key Applications

Security policy: organization have complex infrastructure and need common tooling to reason about the system as a whole

Protocols: expressions are a useful data type and require interoperability across programming languages and platforms.

This implementation has minimal dependencies, runs quickly, and can be embedded into Python-based applications. Specifically, the intent is to be part of Cloud Custodian, C7N, as part of the security policy filter.

Installation

python -m pip install cel-python

You now have the CEL run-time available to Python-based applications.

re2

CEL specifies that regular expressions use re2 syntax, https://github.com/google/re2/wiki/Syntax. As of the 0.4.0 release, the Google-RE2 module is part of the CEL distribution.

Command Line

We can read JSON directly from stdin, making this a bit like jq.

% python -m celpy '.this.from.json * 3 + 3' <<EOF
heredoc> {"this": {"from": {"json": 13}}}
heredoc> EOF
42

It's also a desk calculator, like expr, but with float values:

% python -m celpy -n '355.0 / 113.0'
3.1415929203539825

It's not as sophistcated as bc. But, yes, this has a tiny advantage over python -c '355/113'. Most notably, the ability to embed Google CEL into other contexts where you don't really want Python's power.

It's also capable of decision-making, like test:

% echo '{"status": 3}' | python -m celpy -sb '.status == 0'
false
% echo $?
1

We can provide a -a option to define objects with specific data types. This is particularly helpful for providing protobuf message definitions.

python -m celpy -n --arg x:int=6 --arg y:int=7 'x*y'
42

If you want to see details of evaluation, use -v.

python -m celpy -v -n '[2, 4, 6].map(n, n/2)'
... a lot of output
[1, 2, 3]

Library

To follow the pattern defined in the Go implementation, there's a multi-step process for compiling a CEL expression to create a runnable "program". This program can then be applied to argument values.

>>> import celpy
>>> cel_source = """
... account.balance >= transaction.withdrawal
... || (account.overdraftProtection
... && account.overdraftLimit >= transaction.withdrawal - account.balance)
... """
>>> env = celpy.Environment()
>>> ast = env.compile(cel_source)
>>> prgm = env.program(ast)
>>> context = {
... "account": celpy.json_to_cel({"balance": 500, "overdraftProtection": False}),
... "transaction": celpy.json_to_cel({"withdrawal": 600})
... }
>>> result = prgm.evaluate(context)
>>> result
BoolType(False)

The Python classes are generally based on the object model in https://github.com/google/cel-go These types semantics are slightly different from Python's native semantics. Type coercion is not generally done. Python // truncates toward negative infinity. Go (and CEL) / truncates toward zero.

Development

The parser is based on the grammars used by Go and C++, but processed through Python Lark.

See https://github.com/google/cel-spec/blob/master/doc/langdef.md

https://github.com/google/cel-cpp/blob/master/parser/Cel.g4

https://github.com/google/cel-go/blob/master/parser/gen/CEL.g4

The documentation includes PlantUML diagrams. The Sphinx conf.py provides the location for the PlantUML local JAR file if one is used. Currently, it expects docs/plantuml-asl-1.2025.3.jar. The JAR is not provided in this repository, get one from https://plantuml.com. If you install a different version, update the conf.py to refer to the JAR file you've downloaded.

Notes

CEL provides a number of runtime errors that are mapped to Python exceptions.

  • no_matching_overload: this function has no overload for the types of the arguments.
  • no_such_field: a map or message does not contain the desired field.
  • return error for overflow: integer arithmetic overflows

There are mapped to Python celpy.evaluation.EvalError exception. The args will have a message similar to the CEL error message, as well as an underlying Python exception.

In principle CEL can pre-check types. However, see https://github.com/google/cel-spec/blob/master/doc/langdef.md#gradual-type-checking. Rather than try to pre-check types, we'll rely on Python's implementation.

Example 2

Here's an example with some details:

>>> import celpy
# A list of type names and class bindings used to create an environment.
>>> types = []
>>> env = celpy.Environment(types)
# Parse the code to create the CEL AST.
>>> ast = env.compile("355. / 113.")
# Use the AST and any overriding functions to create an executable program.
>>> functions = {}
>>> prgm = env.program(ast, functions)
# Variable bindings.
>>> activation = {}
# Final evaluation.
>>> try:
... result = prgm.evaluate(activation)
... error = None
... except CELEvalError as ex:
... result = None
... error = ex.args[0]
>>> result # doctest: +ELLIPSIS
DoubleType(3.14159...)

Example 3

See https://github.com/google/cel-go/blob/master/examples/simple_test.go

The model Go we're sticking close to:

d := cel.Declarations(decls.NewVar("name", decls.String))
env, err := cel.NewEnv(d)
if err != nil {
log.Fatalf("environment creation error: %v\\n", err)
}
ast, iss := env.Compile(`"Hello world! I'm " + name + "."`)
// Check iss for compilation errors.
if iss.Err() != nil {
log.Fatalln(iss.Err())
}
prg, err := env.Program(ast)
if err != nil {
log.Fatalln(err)
}
out, _, err := prg.Eval(map[string]interface{}{
"name": "CEL",
})
if err != nil {
log.Fatalln(err)
}
fmt.Println(out)
// Output:Hello world! I'm CEL.

Here's the Pythonic approach, using concept patterned after the Go implementation:

>>> from celpy import *
>>> decls = {"name": celtypes.StringType}
>>> env = Environment(annotations=decls)
>>> ast = env.compile('"Hello world! I\'m " + name + "."')
>>> out = env.program(ast).evaluate({"name": "CEL"})
>>> print(out)
Hello world! I'm CEL.

Contributing

See https://cloudcustodian.io/docs/contribute.html

Code of Conduct

This project adheres to the Open Code of Conduct. By participating, you are expected to honor this code.

About

Pure Python implementation of the Common Expression Language

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' GitHub - benja0rtzzz/cel-python: Pure Python implementation of the Common Expression Language · GitHub
Skip to content

Repository files navigation

cel-python

PyPI: cel-pythonGitHub Actions Build StatusApache License

Pure Python implementation of Google Common Expression Language, https://opensource.google/projects/cel.

The Common Expression Language (CEL) implements common semantics for expression evaluation, enabling different applications to more easily interoperate.

Key Applications

Security policy: organization have complex infrastructure and need common tooling to reason about the system as a whole

Protocols: expressions are a useful data type and require interoperability across programming languages and platforms.

This implementation has minimal dependencies, runs quickly, and can be embedded into Python-based applications. Specifically, the intent is to be part of Cloud Custodian, C7N, as part of the security policy filter.

Installation

python -m pip install cel-python

You now have the CEL run-time available to Python-based applications.

re2

CEL specifies that regular expressions use re2 syntax, https://github.com/google/re2/wiki/Syntax. As of the 0.4.0 release, the Google-RE2 module is part of the CEL distribution.

Command Line

We can read JSON directly from stdin, making this a bit like jq.

% python -m celpy '.this.from.json * 3 + 3' <<EOF
heredoc> {"this": {"from": {"json": 13}}}
heredoc> EOF
42

It's also a desk calculator, like expr, but with float values:

% python -m celpy -n '355.0 / 113.0'
3.1415929203539825

It's not as sophistcated as bc. But, yes, this has a tiny advantage over python -c '355/113'. Most notably, the ability to embed Google CEL into other contexts where you don't really want Python's power.

It's also capable of decision-making, like test:

% echo '{"status": 3}' | python -m celpy -sb '.status == 0'
false
% echo $?
1

We can provide a -a option to define objects with specific data types. This is particularly helpful for providing protobuf message definitions.

python -m celpy -n --arg x:int=6 --arg y:int=7 'x*y'
42

If you want to see details of evaluation, use -v.

python -m celpy -v -n '[2, 4, 6].map(n, n/2)'
... a lot of output
[1, 2, 3]

Library

To follow the pattern defined in the Go implementation, there's a multi-step process for compiling a CEL expression to create a runnable "program". This program can then be applied to argument values.

>>> import celpy
>>> cel_source = """
... account.balance >= transaction.withdrawal
... || (account.overdraftProtection
... && account.overdraftLimit >= transaction.withdrawal - account.balance)
... """
>>> env = celpy.Environment()
>>> ast = env.compile(cel_source)
>>> prgm = env.program(ast)
>>> context = {
... "account": celpy.json_to_cel({"balance": 500, "overdraftProtection": False}),
... "transaction": celpy.json_to_cel({"withdrawal": 600})
... }
>>> result = prgm.evaluate(context)
>>> result
BoolType(False)

The Python classes are generally based on the object model in https://github.com/google/cel-go These types semantics are slightly different from Python's native semantics. Type coercion is not generally done. Python // truncates toward negative infinity. Go (and CEL) / truncates toward zero.

Development

The parser is based on the grammars used by Go and C++, but processed through Python Lark.

See https://github.com/google/cel-spec/blob/master/doc/langdef.md

https://github.com/google/cel-cpp/blob/master/parser/Cel.g4

https://github.com/google/cel-go/blob/master/parser/gen/CEL.g4

The documentation includes PlantUML diagrams. The Sphinx conf.py provides the location for the PlantUML local JAR file if one is used. Currently, it expects docs/plantuml-asl-1.2025.3.jar. The JAR is not provided in this repository, get one from https://plantuml.com. If you install a different version, update the conf.py to refer to the JAR file you've downloaded.

Notes

CEL provides a number of runtime errors that are mapped to Python exceptions.

  • no_matching_overload: this function has no overload for the types of the arguments.
  • no_such_field: a map or message does not contain the desired field.
  • return error for overflow: integer arithmetic overflows

There are mapped to Python celpy.evaluation.EvalError exception. The args will have a message similar to the CEL error message, as well as an underlying Python exception.

In principle CEL can pre-check types. However, see https://github.com/google/cel-spec/blob/master/doc/langdef.md#gradual-type-checking. Rather than try to pre-check types, we'll rely on Python's implementation.

Example 2

Here's an example with some details:

>>> import celpy
# A list of type names and class bindings used to create an environment.
>>> types = []
>>> env = celpy.Environment(types)
# Parse the code to create the CEL AST.
>>> ast = env.compile("355. / 113.")
# Use the AST and any overriding functions to create an executable program.
>>> functions = {}
>>> prgm = env.program(ast, functions)
# Variable bindings.
>>> activation = {}
# Final evaluation.
>>> try:
... result = prgm.evaluate(activation)
... error = None
... except CELEvalError as ex:
... result = None
... error = ex.args[0]
>>> result # doctest: +ELLIPSIS
DoubleType(3.14159...)

Example 3

See https://github.com/google/cel-go/blob/master/examples/simple_test.go

The model Go we're sticking close to:

d := cel.Declarations(decls.NewVar("name", decls.String))
env, err := cel.NewEnv(d)
if err != nil {
log.Fatalf("environment creation error: %v\\n", err)
}
ast, iss := env.Compile(`"Hello world! I'm " + name + "."`)
// Check iss for compilation errors.
if iss.Err() != nil {
log.Fatalln(iss.Err())
}
prg, err := env.Program(ast)
if err != nil {
log.Fatalln(err)
}
out, _, err := prg.Eval(map[string]interface{}{
"name": "CEL",
})
if err != nil {
log.Fatalln(err)
}
fmt.Println(out)
// Output:Hello world! I'm CEL.

Here's the Pythonic approach, using concept patterned after the Go implementation:

>>> from celpy import *
>>> decls = {"name": celtypes.StringType}
>>> env = Environment(annotations=decls)
>>> ast = env.compile('"Hello world! I\'m " + name + "."')
>>> out = env.program(ast).evaluate({"name": "CEL"})
>>> print(out)
Hello world! I'm CEL.

Contributing

See https://cloudcustodian.io/docs/contribute.html

Code of Conduct

This project adheres to the Open Code of Conduct. By participating, you are expected to honor this code.

About

Pure Python implementation of the Common Expression Language

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - benja0rtzzz/cel-python: Pure Python implementation of the Common Expression Language · GitHub
Skip to content

Repository files navigation

cel-python

PyPI: cel-pythonGitHub Actions Build StatusApache License

Pure Python implementation of Google Common Expression Language, https://opensource.google/projects/cel.

The Common Expression Language (CEL) implements common semantics for expression evaluation, enabling different applications to more easily interoperate.

Key Applications

Security policy: organization have complex infrastructure and need common tooling to reason about the system as a whole

Protocols: expressions are a useful data type and require interoperability across programming languages and platforms.

This implementation has minimal dependencies, runs quickly, and can be embedded into Python-based applications. Specifically, the intent is to be part of Cloud Custodian, C7N, as part of the security policy filter.

Installation

python -m pip install cel-python

You now have the CEL run-time available to Python-based applications.

re2

CEL specifies that regular expressions use re2 syntax, https://github.com/google/re2/wiki/Syntax. As of the 0.4.0 release, the Google-RE2 module is part of the CEL distribution.

Command Line

We can read JSON directly from stdin, making this a bit like jq.

% python -m celpy '.this.from.json * 3 + 3' <<EOF
heredoc> {"this": {"from": {"json": 13}}}
heredoc> EOF
42

It's also a desk calculator, like expr, but with float values:

% python -m celpy -n '355.0 / 113.0'
3.1415929203539825

It's not as sophistcated as bc. But, yes, this has a tiny advantage over python -c '355/113'. Most notably, the ability to embed Google CEL into other contexts where you don't really want Python's power.

It's also capable of decision-making, like test:

% echo '{"status": 3}' | python -m celpy -sb '.status == 0'
false
% echo $?
1

We can provide a -a option to define objects with specific data types. This is particularly helpful for providing protobuf message definitions.

python -m celpy -n --arg x:int=6 --arg y:int=7 'x*y'
42

If you want to see details of evaluation, use -v.

python -m celpy -v -n '[2, 4, 6].map(n, n/2)'
... a lot of output
[1, 2, 3]

Library

To follow the pattern defined in the Go implementation, there's a multi-step process for compiling a CEL expression to create a runnable "program". This program can then be applied to argument values.

>>> import celpy
>>> cel_source = """
... account.balance >= transaction.withdrawal
... || (account.overdraftProtection
... && account.overdraftLimit >= transaction.withdrawal - account.balance)
... """
>>> env = celpy.Environment()
>>> ast = env.compile(cel_source)
>>> prgm = env.program(ast)
>>> context = {
... "account": celpy.json_to_cel({"balance": 500, "overdraftProtection": False}),
... "transaction": celpy.json_to_cel({"withdrawal": 600})
... }
>>> result = prgm.evaluate(context)
>>> result
BoolType(False)

The Python classes are generally based on the object model in https://github.com/google/cel-go These types semantics are slightly different from Python's native semantics. Type coercion is not generally done. Python // truncates toward negative infinity. Go (and CEL) / truncates toward zero.

Development

The parser is based on the grammars used by Go and C++, but processed through Python Lark.

See https://github.com/google/cel-spec/blob/master/doc/langdef.md

https://github.com/google/cel-cpp/blob/master/parser/Cel.g4

https://github.com/google/cel-go/blob/master/parser/gen/CEL.g4

The documentation includes PlantUML diagrams. The Sphinx conf.py provides the location for the PlantUML local JAR file if one is used. Currently, it expects docs/plantuml-asl-1.2025.3.jar. The JAR is not provided in this repository, get one from https://plantuml.com. If you install a different version, update the conf.py to refer to the JAR file you've downloaded.

Notes

CEL provides a number of runtime errors that are mapped to Python exceptions.

  • no_matching_overload: this function has no overload for the types of the arguments.
  • no_such_field: a map or message does not contain the desired field.
  • return error for overflow: integer arithmetic overflows

There are mapped to Python celpy.evaluation.EvalError exception. The args will have a message similar to the CEL error message, as well as an underlying Python exception.

In principle CEL can pre-check types. However, see https://github.com/google/cel-spec/blob/master/doc/langdef.md#gradual-type-checking. Rather than try to pre-check types, we'll rely on Python's implementation.

Example 2

Here's an example with some details:

>>> import celpy
# A list of type names and class bindings used to create an environment.
>>> types = []
>>> env = celpy.Environment(types)
# Parse the code to create the CEL AST.
>>> ast = env.compile("355. / 113.")
# Use the AST and any overriding functions to create an executable program.
>>> functions = {}
>>> prgm = env.program(ast, functions)
# Variable bindings.
>>> activation = {}
# Final evaluation.
>>> try:
... result = prgm.evaluate(activation)
... error = None
... except CELEvalError as ex:
... result = None
... error = ex.args[0]
>>> result # doctest: +ELLIPSIS
DoubleType(3.14159...)

Example 3

See https://github.com/google/cel-go/blob/master/examples/simple_test.go

The model Go we're sticking close to:

d := cel.Declarations(decls.NewVar("name", decls.String))
env, err := cel.NewEnv(d)
if err != nil {
log.Fatalf("environment creation error: %v\\n", err)
}
ast, iss := env.Compile(`"Hello world! I'm " + name + "."`)
// Check iss for compilation errors.
if iss.Err() != nil {
log.Fatalln(iss.Err())
}
prg, err := env.Program(ast)
if err != nil {
log.Fatalln(err)
}
out, _, err := prg.Eval(map[string]interface{}{
"name": "CEL",
})
if err != nil {
log.Fatalln(err)
}
fmt.Println(out)
// Output:Hello world! I'm CEL.

Here's the Pythonic approach, using concept patterned after the Go implementation:

>>> from celpy import *
>>> decls = {"name": celtypes.StringType}
>>> env = Environment(annotations=decls)
>>> ast = env.compile('"Hello world! I\'m " + name + "."')
>>> out = env.program(ast).evaluate({"name": "CEL"})
>>> print(out)
Hello world! I'm CEL.

Contributing

See https://cloudcustodian.io/docs/contribute.html

Code of Conduct

This project adheres to the Open Code of Conduct. By participating, you are expected to honor this code.

About

Pure Python implementation of the Common Expression Language

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - benja0rtzzz/cel-python: Pure Python implementation of the Common Expression Language · GitHub
Skip to content

Repository files navigation

cel-python

PyPI: cel-pythonGitHub Actions Build StatusApache License

Pure Python implementation of Google Common Expression Language, https://opensource.google/projects/cel.

The Common Expression Language (CEL) implements common semantics for expression evaluation, enabling different applications to more easily interoperate.

Key Applications

Security policy: organization have complex infrastructure and need common tooling to reason about the system as a whole

Protocols: expressions are a useful data type and require interoperability across programming languages and platforms.

This implementation has minimal dependencies, runs quickly, and can be embedded into Python-based applications. Specifically, the intent is to be part of Cloud Custodian, C7N, as part of the security policy filter.

Installation

python -m pip install cel-python

You now have the CEL run-time available to Python-based applications.

re2

CEL specifies that regular expressions use re2 syntax, https://github.com/google/re2/wiki/Syntax. As of the 0.4.0 release, the Google-RE2 module is part of the CEL distribution.

Command Line

We can read JSON directly from stdin, making this a bit like jq.

% python -m celpy '.this.from.json * 3 + 3' <<EOF
heredoc> {"this": {"from": {"json": 13}}}
heredoc> EOF
42

It's also a desk calculator, like expr, but with float values:

% python -m celpy -n '355.0 / 113.0'
3.1415929203539825

It's not as sophistcated as bc. But, yes, this has a tiny advantage over python -c '355/113'. Most notably, the ability to embed Google CEL into other contexts where you don't really want Python's power.

It's also capable of decision-making, like test:

% echo '{"status": 3}' | python -m celpy -sb '.status == 0'
false
% echo $?
1

We can provide a -a option to define objects with specific data types. This is particularly helpful for providing protobuf message definitions.

python -m celpy -n --arg x:int=6 --arg y:int=7 'x*y'
42

If you want to see details of evaluation, use -v.

python -m celpy -v -n '[2, 4, 6].map(n, n/2)'
... a lot of output
[1, 2, 3]

Library

To follow the pattern defined in the Go implementation, there's a multi-step process for compiling a CEL expression to create a runnable "program". This program can then be applied to argument values.

>>> import celpy
>>> cel_source = """
... account.balance >= transaction.withdrawal
... || (account.overdraftProtection
... && account.overdraftLimit >= transaction.withdrawal - account.balance)
... """
>>> env = celpy.Environment()
>>> ast = env.compile(cel_source)
>>> prgm = env.program(ast)
>>> context = {
... "account": celpy.json_to_cel({"balance": 500, "overdraftProtection": False}),
... "transaction": celpy.json_to_cel({"withdrawal": 600})
... }
>>> result = prgm.evaluate(context)
>>> result
BoolType(False)

The Python classes are generally based on the object model in https://github.com/google/cel-go These types semantics are slightly different from Python's native semantics. Type coercion is not generally done. Python // truncates toward negative infinity. Go (and CEL) / truncates toward zero.

Development

The parser is based on the grammars used by Go and C++, but processed through Python Lark.

See https://github.com/google/cel-spec/blob/master/doc/langdef.md

https://github.com/google/cel-cpp/blob/master/parser/Cel.g4

https://github.com/google/cel-go/blob/master/parser/gen/CEL.g4

The documentation includes PlantUML diagrams. The Sphinx conf.py provides the location for the PlantUML local JAR file if one is used. Currently, it expects docs/plantuml-asl-1.2025.3.jar. The JAR is not provided in this repository, get one from https://plantuml.com. If you install a different version, update the conf.py to refer to the JAR file you've downloaded.

Notes

CEL provides a number of runtime errors that are mapped to Python exceptions.

  • no_matching_overload: this function has no overload for the types of the arguments.
  • no_such_field: a map or message does not contain the desired field.
  • return error for overflow: integer arithmetic overflows

There are mapped to Python celpy.evaluation.EvalError exception. The args will have a message similar to the CEL error message, as well as an underlying Python exception.

In principle CEL can pre-check types. However, see https://github.com/google/cel-spec/blob/master/doc/langdef.md#gradual-type-checking. Rather than try to pre-check types, we'll rely on Python's implementation.

Example 2

Here's an example with some details:

>>> import celpy
# A list of type names and class bindings used to create an environment.
>>> types = []
>>> env = celpy.Environment(types)
# Parse the code to create the CEL AST.
>>> ast = env.compile("355. / 113.")
# Use the AST and any overriding functions to create an executable program.
>>> functions = {}
>>> prgm = env.program(ast, functions)
# Variable bindings.
>>> activation = {}
# Final evaluation.
>>> try:
... result = prgm.evaluate(activation)
... error = None
... except CELEvalError as ex:
... result = None
... error = ex.args[0]
>>> result # doctest: +ELLIPSIS
DoubleType(3.14159...)

Example 3

See https://github.com/google/cel-go/blob/master/examples/simple_test.go

The model Go we're sticking close to:

d := cel.Declarations(decls.NewVar("name", decls.String))
env, err := cel.NewEnv(d)
if err != nil {
log.Fatalf("environment creation error: %v\\n", err)
}
ast, iss := env.Compile(`"Hello world! I'm " + name + "."`)
// Check iss for compilation errors.
if iss.Err() != nil {
log.Fatalln(iss.Err())
}
prg, err := env.Program(ast)
if err != nil {
log.Fatalln(err)
}
out, _, err := prg.Eval(map[string]interface{}{
"name": "CEL",
})
if err != nil {
log.Fatalln(err)
}
fmt.Println(out)
// Output:Hello world! I'm CEL.

Here's the Pythonic approach, using concept patterned after the Go implementation:

>>> from celpy import *
>>> decls = {"name": celtypes.StringType}
>>> env = Environment(annotations=decls)
>>> ast = env.compile('"Hello world! I\'m " + name + "."')
>>> out = env.program(ast).evaluate({"name": "CEL"})
>>> print(out)
Hello world! I'm CEL.

Contributing

See https://cloudcustodian.io/docs/contribute.html

Code of Conduct

This project adheres to the Open Code of Conduct. By participating, you are expected to honor this code.

About

Pure Python implementation of the Common Expression Language

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); GitHub - benja0rtzzz/cel-python: Pure Python implementation of the Common Expression Language · GitHub
Skip to content

Repository files navigation

cel-python

PyPI: cel-pythonGitHub Actions Build StatusApache License

Pure Python implementation of Google Common Expression Language, https://opensource.google/projects/cel.

The Common Expression Language (CEL) implements common semantics for expression evaluation, enabling different applications to more easily interoperate.

Key Applications

Security policy: organization have complex infrastructure and need common tooling to reason about the system as a whole

Protocols: expressions are a useful data type and require interoperability across programming languages and platforms.

This implementation has minimal dependencies, runs quickly, and can be embedded into Python-based applications. Specifically, the intent is to be part of Cloud Custodian, C7N, as part of the security policy filter.

Installation

python -m pip install cel-python

You now have the CEL run-time available to Python-based applications.

re2

CEL specifies that regular expressions use re2 syntax, https://github.com/google/re2/wiki/Syntax. As of the 0.4.0 release, the Google-RE2 module is part of the CEL distribution.

Command Line

We can read JSON directly from stdin, making this a bit like jq.

% python -m celpy '.this.from.json * 3 + 3' <<EOF
heredoc> {"this": {"from": {"json": 13}}}
heredoc> EOF
42

It's also a desk calculator, like expr, but with float values:

% python -m celpy -n '355.0 / 113.0'
3.1415929203539825

It's not as sophistcated as bc. But, yes, this has a tiny advantage over python -c '355/113'. Most notably, the ability to embed Google CEL into other contexts where you don't really want Python's power.

It's also capable of decision-making, like test:

% echo '{"status": 3}' | python -m celpy -sb '.status == 0'
false
% echo $?
1

We can provide a -a option to define objects with specific data types. This is particularly helpful for providing protobuf message definitions.

python -m celpy -n --arg x:int=6 --arg y:int=7 'x*y'
42

If you want to see details of evaluation, use -v.

python -m celpy -v -n '[2, 4, 6].map(n, n/2)'
... a lot of output
[1, 2, 3]

Library

To follow the pattern defined in the Go implementation, there's a multi-step process for compiling a CEL expression to create a runnable "program". This program can then be applied to argument values.

>>> import celpy
>>> cel_source = """
... account.balance >= transaction.withdrawal
... || (account.overdraftProtection
... && account.overdraftLimit >= transaction.withdrawal - account.balance)
... """
>>> env = celpy.Environment()
>>> ast = env.compile(cel_source)
>>> prgm = env.program(ast)
>>> context = {
... "account": celpy.json_to_cel({"balance": 500, "overdraftProtection": False}),
... "transaction": celpy.json_to_cel({"withdrawal": 600})
... }
>>> result = prgm.evaluate(context)
>>> result
BoolType(False)

The Python classes are generally based on the object model in https://github.com/google/cel-go These types semantics are slightly different from Python's native semantics. Type coercion is not generally done. Python // truncates toward negative infinity. Go (and CEL) / truncates toward zero.

Development

The parser is based on the grammars used by Go and C++, but processed through Python Lark.

See https://github.com/google/cel-spec/blob/master/doc/langdef.md

https://github.com/google/cel-cpp/blob/master/parser/Cel.g4

https://github.com/google/cel-go/blob/master/parser/gen/CEL.g4

The documentation includes PlantUML diagrams. The Sphinx conf.py provides the location for the PlantUML local JAR file if one is used. Currently, it expects docs/plantuml-asl-1.2025.3.jar. The JAR is not provided in this repository, get one from https://plantuml.com. If you install a different version, update the conf.py to refer to the JAR file you've downloaded.

Notes

CEL provides a number of runtime errors that are mapped to Python exceptions.

  • no_matching_overload: this function has no overload for the types of the arguments.
  • no_such_field: a map or message does not contain the desired field.
  • return error for overflow: integer arithmetic overflows

There are mapped to Python celpy.evaluation.EvalError exception. The args will have a message similar to the CEL error message, as well as an underlying Python exception.

In principle CEL can pre-check types. However, see https://github.com/google/cel-spec/blob/master/doc/langdef.md#gradual-type-checking. Rather than try to pre-check types, we'll rely on Python's implementation.

Example 2

Here's an example with some details:

>>> import celpy
# A list of type names and class bindings used to create an environment.
>>> types = []
>>> env = celpy.Environment(types)
# Parse the code to create the CEL AST.
>>> ast = env.compile("355. / 113.")
# Use the AST and any overriding functions to create an executable program.
>>> functions = {}
>>> prgm = env.program(ast, functions)
# Variable bindings.
>>> activation = {}
# Final evaluation.
>>> try:
... result = prgm.evaluate(activation)
... error = None
... except CELEvalError as ex:
... result = None
... error = ex.args[0]
>>> result # doctest: +ELLIPSIS
DoubleType(3.14159...)

Example 3

See https://github.com/google/cel-go/blob/master/examples/simple_test.go

The model Go we're sticking close to:

d := cel.Declarations(decls.NewVar("name", decls.String))
env, err := cel.NewEnv(d)
if err != nil {
log.Fatalf("environment creation error: %v\\n", err)
}
ast, iss := env.Compile(`"Hello world! I'm " + name + "."`)
// Check iss for compilation errors.
if iss.Err() != nil {
log.Fatalln(iss.Err())
}
prg, err := env.Program(ast)
if err != nil {
log.Fatalln(err)
}
out, _, err := prg.Eval(map[string]interface{}{
"name": "CEL",
})
if err != nil {
log.Fatalln(err)
}
fmt.Println(out)
// Output:Hello world! I'm CEL.

Here's the Pythonic approach, using concept patterned after the Go implementation:

>>> from celpy import *
>>> decls = {"name": celtypes.StringType}
>>> env = Environment(annotations=decls)
>>> ast = env.compile('"Hello world! I\'m " + name + "."')
>>> out = env.program(ast).evaluate({"name": "CEL"})
>>> print(out)
Hello world! I'm CEL.

Contributing

See https://cloudcustodian.io/docs/contribute.html

Code of Conduct

This project adheres to the Open Code of Conduct. By participating, you are expected to honor this code.

About

Pure Python implementation of the Common Expression Language

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages