Complete autogenerated reference for the Python CEL library.
::: cel.evaluate
Compile a CEL expression into a reusable Program object.
This function parses and compiles a CEL expression, returning a Program object that can be executed multiple times with different contexts. This is more efficient than calling evaluate() repeatedly with the same expression.
Parameters:
expression: The CEL expression to compile
Returns:
- A compiled
Programobject
Raises:
ValueError: If the expression has syntax errors or is malformed
Example:
import cel
# Compile once
program = cel.compile("x + y")
# Execute many times with different contexts
result1 = program.execute({"x": 1, "y": 2})
assert result1 == 3 # → 3
result2 = program.execute({"x": 10, "y": 20})
assert result2 == 30 # → 30When to use compile() vs evaluate():
- Use
evaluate()for one-time evaluation or interactive/REPL usage - Use
compile()+execute()when evaluating the same expression with many different contexts or in performance-critical loops
A compiled CEL program that can be executed multiple times with different contexts.
The Program class represents a pre-compiled CEL expression. Use this when you need to evaluate the same expression many times with different variable bindings. Compiling once and executing multiple times is significantly faster than calling evaluate() repeatedly.
import cel
# Compile the expression once
program = cel.compile("price * quantity > 100")
# Execute many times with different contexts
result1 = program.execute({"price": 10, "quantity": 20})
assert result1 == True # → True (200 > 100)
result2 = program.execute({"price": 5, "quantity": 10})
assert result2 == False # → False (50 > 100)Execute the compiled program with the given context.
Parameters:
context: Optional evaluation context (dict or Context object)
Returns:
- The result of the expression evaluation
Raises:
RuntimeError: If a variable or function is undefinedTypeError: If there's a type mismatch during executionValueError: If the context is an invalid type
Example with dict context:
import cel
program = cel.compile("user.name + ' is ' + user.role")
result = program.execute({
"user": {"name": "Alice", "role": "admin"}
})
assert result == "Alice is admin"Example with Context object:
import cel
from cel import Context
program = cel.compile("greet(name)")
ctx = Context()
ctx.add_variable("name", "World")
ctx.add_function("greet", lambda x: f"Hello, {x}!")
result = program.execute(ctx)
assert result == "Hello, World!"Performance pattern - compile once, execute many:
import cel
# Access control policy - compiled once at startup
policy = cel.compile(
'user.role == "admin" || resource.owner == user.id'
)
# Evaluated many times per request
def check_access(user, resource):
return policy.execute({"user": user, "resource": resource})
# Fast repeated evaluation
assert check_access({"id": "alice", "role": "admin"}, {"owner": "bob"}) == True
assert check_access({"id": "bob", "role": "user"}, {"owner": "bob"}) == True
assert check_access({"id": "charlie", "role": "user"}, {"owner": "bob"}) == FalseWrapper for CEL optional values.
CEL optional values preserve the distinction between "no value" and "a value that is null". The Python wrapper keeps that distinction intact.
import cel
opt = cel.evaluate("optional.of(42)")
assert isinstance(opt, cel.OptionalValue)
assert opt.has_value() is True
assert opt.value() == 42
assert opt.or_value(0) == 42
none_opt = cel.evaluate("optional.none()")
assert none_opt.has_value() is False
assert none_opt.or_value("default") == "default"Distinguishing optional.none() from optional.of(null):
import cel
opt_null = cel.evaluate("optional.of(null)")
assert opt_null.has_value() is True
assert opt_null.value() is None
opt_none = cel.evaluate("optional.none()")
assert opt_none.has_value() is FalsePassing OptionalValue into evaluation contexts:
import cel
opt = cel.OptionalValue.of(123)
assert cel.evaluate("opt.orValue(0)", {"opt": opt}) == 123
opt_none = cel.OptionalValue.none()
assert cel.evaluate("opt.orValue(7)", {"opt": opt_none}) == 7Create an optional value containing value.
Create an empty optional value.
Return True when the optional contains a value.
Return the contained value or raise ValueError for optional.none().
Return the contained value if present, otherwise default.
Return self if it has a value, otherwise return other.
A class for managing evaluation context with variables and custom functions.
The Context class provides more control over the evaluation environment than simple dictionary context. It allows you to:
- Add variables with type checking
- Register custom Python functions
- Manage complex evaluation scenarios
from cel import evaluate, Context
# Basic usage
context = Context()
context.add_variable("name", "Alice")
context.add_variable("age", 30)
result = evaluate("name + ' is ' + string(age)", context)
# → "Alice is 30"
assert result == "Alice is 30"Add a variable to the context.
Parameters:
name: Variable name (must be valid CEL identifier)value: Variable value (will be converted to appropriate CEL type)
Example:
from cel import Context, evaluate
context = Context()
context.add_variable("user_id", "123")
context.add_variable("permissions", ["read", "write"])
context.add_variable("config", {"debug": True, "port": 8080})
# Verify the variables are accessible
evaluate("user_id", context)
# → "123"
evaluate("size(permissions)", context)
# → 2
evaluate("config.debug", context)
# → True
assert evaluate("user_id", context) == "123"
assert evaluate("size(permissions)", context) == 2
assert evaluate("config.debug", context) == TrueAdd multiple variables at once.
Parameters:
variables: Dictionary of variable names to values
Example:
from cel import Context, evaluate
context = Context()
context.update({
"user_id": "123",
"role": "admin",
"permissions": ["read", "write", "delete"]
})
# Verify the batch update worked
evaluate("user_id", context)
# → "123"
evaluate("role", context)
# → "admin"
evaluate("size(permissions)", context)
# → 3
assert evaluate("user_id", context) == "123"
assert evaluate("role", context) == "admin"
assert evaluate("size(permissions)", context) == 3Register a Python function for use in CEL expressions.
Parameters:
name: Function name as it will appear in CEL expressionsfunc: Python function to register
Requirements for functions:
- Should handle type conversions appropriately
- Should raise meaningful exceptions for invalid inputs
- Must be callable from the Python environment
Example:
from cel import Context, evaluate
def validate_email(email):
import re
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
return re.match(pattern, email) is not None
context = Context()
context.add_function("validate_email", validate_email)
evaluate('validate_email("user@example.com")', context)
# → True
# Test invalid email
evaluate('validate_email("invalid-email")', context)
# → False
result = evaluate('validate_email("user@example.com")', context)
assert result == True
result = evaluate('validate_email("invalid-email")', context)
assert result == FalseThis table shows how CEL types are converted to Python types when expressions are evaluated:
| CEL Type | CEL Spec | Python Type | Example CEL | Python Result |
|---|---|---|---|---|
int |
64-bit signed integers | int |
42 |
42 |
uint |
64-bit unsigned integers | int |
42u |
42 |
double |
64-bit IEEE floating-point | float |
3.14 |
3.14 |
bool |
Boolean values | bool |
true |
True |
string |
Unicode code point sequences | str |
"hello" |
"hello" |
bytes |
Byte sequences | bytes |
b"data" |
b"data" |
null_type |
Null value | NoneType |
null |
None |
list |
Ordered sequences | list |
[1, 2, 3] |
[1, 2, 3] |
map |
Key-value collections | dict |
{"key": "value"} |
{"key": "value"} |
timestamp |
Protocol buffer timestamps | datetime.datetime |
timestamp("2024-01-01T00:00:00Z") |
datetime(2024, 1, 1, tzinfo=timezone.utc) |
duration |
Protocol buffer durations | datetime.timedelta |
duration("1h30m") |
timedelta(hours=1, minutes=30) |
✅ FULLY COMPLIANT with CEL specification:
- Key Types: Restricted to
int,uint,bool, andstringas per CEL spec - Value Types: Support heterogeneous values (mixed types) as allowed by CEL spec
- Runtime Behavior: Maps can contain
dyntypes for mixed-value collections
Examples:
// ✅ Valid key types
{1: "int key", "str": "string key", true: "bool key"}
// ✅ Mixed value types (CEL compliant)
{"name": "Alice", "age": 30, "verified": true, "score": 95.5}
// ✅ Nested heterogeneous structures
{"users": [{"name": "Alice"}, {"name": "Bob"}], "count": 2}
When passing Python objects as context:
| Python Type | CEL Type | Notes |
|---|---|---|
int |
int |
Direct mapping |
float |
double |
Direct mapping |
str |
string |
Direct mapping |
bool |
bool |
Direct mapping |
None |
null |
Direct mapping |
list |
list(T) |
Element types preserved |
dict |
map(K, V) |
Key/value types preserved |
bytes |
bytes |
Direct mapping |
datetime.datetime |
timestamp |
Timezone info preserved |
datetime.timedelta |
duration |
Direct mapping |
The library raises specific exception types for different error conditions based on the underlying error type:
Raised when the CEL expression has invalid syntax, is empty, or fails to compile:
from cel import evaluate
# Invalid syntax raises ValueError
try:
evaluate("1 + + 2") # Invalid syntax
# → ValueError: Failed to parse expression: ...
assert False, "Should have raised ValueError"
except ValueError as e:
assert "Failed to parse expression" in str(e)
# Empty expression raises ValueError
try:
evaluate("")
# → ValueError: Failed to parse expression
assert False, "Should have raised ValueError"
except ValueError as e:
assert "Failed to parse expression" in str(e)Raised for undefined variables or functions, and function execution errors:
from cel import evaluate
# Undefined variables raise RuntimeError
try:
evaluate("unknown_variable + 1", {})
# → RuntimeError: Undefined variable 'unknown_variable'
assert False, "Should have raised RuntimeError"
except RuntimeError as e:
assert "Undefined variable" in str(e)
# Undefined functions raise RuntimeError
try:
evaluate("unknownFunction(42)", {})
# → RuntimeError: Undefined function 'unknownFunction'
assert False, "Should have raised RuntimeError"
except RuntimeError as e:
assert "Undefined" in str(e) and "function" in str(e)
# Function execution errors raise RuntimeError
from cel import Context
def failing_function():
raise Exception("Something went wrong")
context = Context()
context.add_function("fail", failing_function)
try:
evaluate("fail()", context)
# → RuntimeError: Function 'fail' error: Something went wrong
assert False, "Should have raised RuntimeError"
except RuntimeError as e:
assert "Function 'fail' error" in str(e)Raised when operations are performed on incompatible types:
from cel import evaluate
# String + int operations raise TypeError
try:
evaluate('"hello" + 42') # String + int
# → TypeError: Unsupported addition operation between string and int
assert False, "Should have raised TypeError"
except TypeError as e:
assert "Unsupported addition operation" in str(e)
# Mixed signed/unsigned int operations raise TypeError
try:
evaluate("1u + 2") # Mixed signed/unsigned int
# → TypeError: Cannot mix signed and unsigned integers
assert False, "Should have raised TypeError"
except TypeError as e:
assert "Cannot mix signed and unsigned integers" in str(e)
# Unsupported multiplication raises TypeError
try:
evaluate('"text" * "more"') # String multiplication
# → TypeError: Unsupported multiplication operation between strings
assert False, "Should have raised TypeError"
except TypeError as e:
assert "Unsupported multiplication operation" in str(e)Mixed numeric types raise TypeError:
from cel import evaluate
# Mixed numeric types in expressions
try:
evaluate("1 + 2.5") # int + double
except TypeError as e:
assert "Unsupported addition operation" in str(e)
print(f"Mixed arithmetic error: {e}")
# Mixed types from context
context = {"int_val": 10, "float_val": 2.5}
try:
evaluate("int_val * float_val", context)
except TypeError as e:
assert "Unsupported multiplication operation" in str(e)
print(f"Context type mixing error: {e}")
# To fix mixed arithmetic, use consistent types:
result = evaluate("1.0 + 2.5") # → 3.5 (both doubles)
result = evaluate("1 + 2") # → 3 (both ints)For comprehensive error handling patterns, safety guidelines, and production best practices, see the Error Handling How-To Guide which covers:
- Safe handling of malformed expressions and untrusted input
- Safe evaluation wrappers and best practices
- Context validation patterns
- Defensive expression techniques
- Logging and monitoring
- Testing error scenarios