Skip to main content

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 evaluation
  • parse(expression): Parse a CEL expression into an AST
  • evaluate(ast, context=None, allow_undeclared_vars=True): Evaluate a parsed CEL expression

Classes

  • Evaluator: Main class for evaluating CEL expressions
  • CELSyntaxError: Exception raised for syntax errors
  • CELEvaluationError: Base exception for evaluation errors
  • CELTypeError: Exception raised for type errors
  • CELUndefinedError: Exception raised for undefined variables

Built-in Functions

  • size(obj): Get the size of a string, list, or map
  • contains(container, item): Check if a container contains an item
  • startsWith(s, prefix) / starts_with(s, prefix): Check if a string starts with a prefix
  • endsWith(s, suffix) / ends_with(s, suffix): Check if a string ends with a suffix
  • matches(s, pattern): Check if a string matches a regex pattern
  • int(value): Convert a value to an integer
  • float(value): Convert a value to a float
  • bool(value): Convert a value to a boolean
  • string(value): Convert a value to a string
  • type(value): Get the type of a value as a string

Development

Setup

  1. Clone the repository
  2. Install dependencies using uv sync --all-extras --dev
  3. 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:

  1. Update the version number in setup.py
  2. Create a new release on GitHub with a tag matching the version (e.g., v0.1.0)
  3. 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:

  1. Create an API token on PyPI (https://pypi.org/manage/account/token/)
  2. Add the token as a GitHub secret named PYPI_API_TOKEN in the repository settings

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

celparser-0.1.0.tar.gz (20.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

celparser-0.1.0-py3-none-any.whl (20.4 kB view details)

Uploaded Python 3

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

Hashes for celparser-0.1.0.tar.gz
Algorithm Hash digest
SHA256 91152642306f39408c6d5bc18b26d3906479ccf0064c0e802d6459eff1f90f60
MD5 edf686fc7d3730c3a6b38cf802443d73
BLAKE2b-256 505a4537ab32d847a57f7e4f17eb83c4be5560cb091d847c4a1a3a72c71fe36b

See more details on using hashes here.

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

Hashes for celparser-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ce735ded3a8d63fd27876e0c10d12f227dd7236b4446d3d71511b2017beb1d54
MD5 cba8c9dbb718d13801617089d592325c
BLAKE2b-256 9149f27a689fe3ef170a6ac2be21093b1fcde1cf7759ec48a7867010d38727ba

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page