Fast, Safe, and Expressive evaluation of Google's Common Expression Language (CEL) in Python, powered by Rust.
The Common Expression Language (CEL) is a non-Turing complete language designed for simplicity, speed, and safety. This Python package wraps the Rust implementation cel v0.14.0, providing microsecond-level expression evaluation with seamless Python integration.
- 🛡️ Policy Enforcement: Define access control rules that can be updated without code changes
- ⚙️ Configuration Validation: Validate complex settings with declarative rules
- 🔄 Data Transformation: Transform and filter data with safe, portable expressions
- 📋 Business Rules: Implement decision logic that business users can understand
- 🔍 Query Filtering: Build dynamic filters for databases and APIs
- 🎯 Feature Flags: Create sophisticated feature toggle conditions
pip install common-expression-languageOr using uv:
uv add common-expression-languageAfter installation, both the Python library and the cel command-line tool will be available.
📖 Full Documentation: https://python-common-expression-language.readthedocs.io/
fromcelimportevaluate# Simple expressionsresult=evaluate("1 + 2") # 3result=evaluate("'Hello ' + 'World'") # "Hello World"result=evaluate("age >= 18", {"age": 25}) # True# Complex expressions with contextresult=evaluate(
'user.role == "admin" && "write" in permissions',
{
"user": {"role": "admin"},
"permissions": ["read", "write", "delete"]
}
) # True# Simple evaluation
cel '1 + 2'# 3# With context
cel 'age >= 18' --context '{"age": 25}'# true# Interactive REPL
cel --interactiveWhen evaluating the same expression multiple times with different contexts, use compile() for better performance:
importcel# Compile onceprogram=cel.compile("price * quantity > threshold")
# Execute many times - much faster than repeated evaluate() callsresult1=program.execute({"price": 10, "quantity": 5, "threshold": 40}) # Trueresult2=program.execute({"price": 5, "quantity": 3, "threshold": 20}) # FalsefromcelimportContext, evaluatedefcalculate_discount(price, rate):
returnprice*ratecontext=Context()
context.add_function("calculate_discount", calculate_discount)
context.add_variable("price", 100)
result=evaluate("price - calculate_discount(price, 0.1)", context) # 90.0fromcelimportevaluate, Context# Access control policypolicy="""user.role == "admin" || (resource.owner == user.id && current_hour >= 9 && current_hour <= 17)"""context=Context()
context.update({
"user": {"id": "alice", "role": "user"},
"resource": {"owner": "alice"},
"current_hour": 14# 2 PM
})
access_granted=evaluate(policy, context) # True- ✅ Fast Evaluation: Microsecond-level expression evaluation via Rust
- ✅ Rich Type System: Integers, floats, strings, lists, maps, timestamps, durations, bytes, optionals
- ✅ Python Integration: Seamless type conversion and custom function support (callable as
f(x)orx.f()) - ✅ Extended Standard Library: Optional
strings,math,sets,encodersandlistsextensions that mirror cel-go (seecel.stdlib) - ✅ Static Analysis: Inspect the variables and functions an expression references before running it (
Program.references()) - ✅ CLI Tools: Interactive REPL and batch processing capabilities
- ✅ Safety First: Non-Turing complete, safe for untrusted expressions
importcelprogram=cel.compile("resource.owner == user.id && size(roles) > 0")
program.variables() # ['resource', 'roles', 'user']program.functions() # ['_&&_', '_==_', '_>_', 'size']importcelfromcel.stdlibimportadd_stdlib_to_contextctx=cel.Context()
add_stdlib_to_context(ctx) # adds strings, math, sets, encoders, listscel.evaluate('"Hello World".lowerAscii()', ctx) # 'hello world'cel.evaluate("math.greatest([3, 1, 2])", ctx) # 3cel.evaluate("[1, 2, 3].contains(2)", ctx) # Truecel.evaluate('base64.encode(b"hi")', ctx) # 'aGk='Portability note: CEL has no portable "bytecode". Cross-implementation interchange in the CEL ecosystem uses the protobuf AST (
cel.expr.Expr/CheckedExpr), which the upstreamcelRust crate does not yet produce or consume. The portable artifact for this library is the CEL source string; useProgram.references()for static analysis. See the docs for details.
📚 Complete documentation available at: https://python-common-expression-language.readthedocs.io/
To build and serve the documentation locally:
# Install documentation dependencies
uv sync --group docs
# Build the documentation
uv run --group docs mkdocs build
# Serve locally with live reload
uv run --group docs mkdocs serveThe documentation will be available at http://localhost:8000
# Run all tests
uv run pytest
# Run with coverage
uv run pytest --cov=cel
# Test all documentation examples (embedded code + standalone files)
uv run --group docs pytest tests/test_docs.py -v# Install development dependencies
uv sync --dev
# Build the package
uv run maturin develop
# Run tests
uv run pytestContributions are welcome! Please see our documentation for:
- CHANGELOG — release notes and behaviour changes
- Development setup and guidelines
- Areas where help is needed
This project is licensed under the same terms as the original cel-interpreter crate.