Python parser and evaluator for Google Common Expression Language (CEL)
Project description
celparser - Python Common Expression Language Parser
A Python implementation of Google's Common Expression Language (CEL) parser and evaluator.
Overview
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.
Features
- 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
Installation
Using pip
pip install celparser
Using uv
uv pip install celparser
From source
git clone https://github.com/celparser/celparser.git
cd celparser
pip install .
Requirements
- Python 3.8 or higher
Usage Examples
Basic Usage
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: 11
Using parse and evaluate directly
from 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: 30
Working with Different Data Types
from celparser import compile
context = {
"name": "Alice",
"age": 30,
"isAdmin": True,
"tags": ["user", "member"],
"profile": {
"email": "alice@example.com",
"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: "alice@example.com"
# 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: True
Error Handling
from 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}")
Complex Example: Permission Checking
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)"
API Reference
Main Functions
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
Classes
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
Built-in Functions
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
Development
Setup
- Clone the repository
- Install dependencies using
uv sync --all-extras --dev - Run tests using
uv run pytest tests
Publishing to PyPI
This project uses GitHub Actions to automatically publish to PyPI when a new release is created.
For Maintainers
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
Setting up PyPI API Token
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
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file celparser-0.1.0.tar.gz.
File metadata
- Download URL: celparser-0.1.0.tar.gz
- Upload date:
- Size: 20.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.6.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
91152642306f39408c6d5bc18b26d3906479ccf0064c0e802d6459eff1f90f60
|
|
| MD5 |
edf686fc7d3730c3a6b38cf802443d73
|
|
| BLAKE2b-256 |
505a4537ab32d847a57f7e4f17eb83c4be5560cb091d847c4a1a3a72c71fe36b
|
File details
Details for the file celparser-0.1.0-py3-none-any.whl.
File metadata
- Download URL: celparser-0.1.0-py3-none-any.whl
- Upload date:
- Size: 20.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.6.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ce735ded3a8d63fd27876e0c10d12f227dd7236b4446d3d71511b2017beb1d54
|
|
| MD5 |
cba8c9dbb718d13801617089d592325c
|
|
| BLAKE2b-256 |
9149f27a689fe3ef170a6ac2be21093b1fcde1cf7759ec48a7867010d38727ba
|