A Python implementation of Google's Common Expression Language (CEL) parser and evaluator.
celparser is a Python package that provides parsing and evaluation of Google's Common Expression Language (CEL). CEL is a simple expression language that lets you check whether a condition is true or false at runtime. It's designed to be simple, portable, and safe to execute in constrained environments.
CEL is used in various Google products and open-source projects for policy enforcement, data filtering, and configuration.
- Parse and evaluate CEL expressions
- Support for basic CEL syntax and operations
- Support for common data types (string, number, boolean, lists, maps)
- Comprehensive error reporting
- Simple API for integration with Python applications
- Built-in functions for common operations
- Extensible with custom functions
pip install celparseruv pip install celparsergit clone https://github.com/celparser/celparser.git
cd celparser
pip install .- Python 3.8 or higher
from celparser import compile
# Compile an expression
expression = compile("a + b * 2")
# Evaluate with a context
result = expression({"a": 10, "b": 5})
print(result) # Output: 20
# Reuse the same expression with different contexts
result2 = expression({"a": 5, "b": 3})
print(result2) # Output: 11from celparser.parser import parse
from celparser.evaluator import evaluate
# Parse the expression into an AST
ast = parse("(a + b) * 2")
# Evaluate the AST with a context
result = evaluate(ast, {"a": 10, "b": 5})
print(result) # Output: 30from celparser import compile
context = {
"name": "Alice",
"age": 30,
"isAdmin": True,
"tags": ["user", "member"],
"profile": {
"email": "[email protected]",
"active": True
}
}
# String concatenation
expr1 = compile("name + ' is ' + string(age) + ' years old'")
print(expr1(context)) # Output: "Alice is 30 years old"
# Ternary operator
expr2 = compile("isAdmin ? 'Administrator' : 'Regular user'")
print(expr2(context)) # Output: "Administrator"
# List indexing
expr3 = compile("tags[0] + ' account'")
print(expr3(context)) # Output: "user account"
# Map access
expr4 = compile("profile.email")
print(expr4(context)) # Output: "[email protected]"
# Built-in functions
expr5 = compile("size(tags)")
print(expr5(context)) # Output: 2
expr6 = compile("contains(tags, 'admin')")
print(expr6(context)) # Output: False
expr7 = compile("type(age)")
print(expr7(context)) # Output: "int"
expr8 = compile("startsWith(name, 'A')")
print(expr8(context)) # Output: Truefrom celparser import compile
from celparser.errors import CELSyntaxError, CELEvaluationError
# Syntax error
try:
expr = compile("a + * b")
except CELSyntaxError as e:
print(f"Syntax error caught: {e}")
# Evaluation error (division by zero)
try:
expr = compile("a / b")
result = expr({"a": 10, "b": 0})
except CELEvaluationError as e:
print(f"Evaluation error caught: {e}")
# Type error
try:
expr = compile("a < b")
result = expr({"a": 10, "b": "not a number"})
except CELEvaluationError as e:
print(f"Type error caught: {e}")
# Undefined variable
try:
expr = compile("a + b", allow_undeclared_vars=False)
result = expr({"a": 10}) # 'b' is missing
except CELEvaluationError as e:
print(f"Undefined variable error caught: {e}")from celparser import compile
# User data
user = {
"name": "Alice",
"role": "editor",
"department": "Engineering",
"permissions": ["read", "write"],
"active": True,
"manager": {
"name": "Bob",
"role": "admin"
},
"projects": [
{"id": "proj1", "access": "full"},
{"id": "proj2", "access": "read-only"}
]
}
# Complex permission check
permission_check = compile("""
active &&
(role == 'admin' ||
(contains(permissions, 'write') &&
(department == 'Engineering' || manager.role == 'admin')))
""")
has_permission = permission_check(user)
print(f"User has required permissions: {has_permission}") # Output: True
# Complex data access and manipulation
project_info = compile("""
size(projects) > 0 ?
projects[0].id + ' (' + projects[0].access + ')' :
'No projects'
""")
result = project_info(user)
print(f"First project info: {result}") # Output: "proj1 (full)"compile(expression, allow_undeclared_vars=True): Compile a CEL expression for later evaluationparse(expression): Parse a CEL expression into an ASTevaluate(ast, context=None, allow_undeclared_vars=True): Evaluate a parsed CEL expression
Evaluator: Main class for evaluating CEL expressionsCELSyntaxError: Exception raised for syntax errorsCELEvaluationError: Base exception for evaluation errorsCELTypeError: Exception raised for type errorsCELUndefinedError: Exception raised for undefined variables
size(obj): Get the size of a string, list, or mapcontains(container, item): Check if a container contains an itemstartsWith(s, prefix)/starts_with(s, prefix): Check if a string starts with a prefixendsWith(s, suffix)/ends_with(s, suffix): Check if a string ends with a suffixmatches(s, pattern): Check if a string matches a regex patternint(value): Convert a value to an integerfloat(value): Convert a value to a floatbool(value): Convert a value to a booleanstring(value): Convert a value to a stringtype(value): Get the type of a value as a string
- Clone the repository
- Install dependencies using
uv sync --all-extras --dev - Run tests using
uv run pytest tests
This project uses GitHub Actions to automatically publish to PyPI when a new release is created.
To publish a new version to PyPI:
- Update the version number in
setup.py - Create a new release on GitHub with a tag matching the version (e.g.,
v0.1.0) - The GitHub Action will automatically build and publish the package to PyPI
To set up the PyPI API token for automated publishing:
- Create an API token on PyPI (https://pypi.org/manage/account/token/)
- Add the token as a GitHub secret named
PYPI_API_TOKENin the repository settings