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.2.tar.gz (20.3 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.2-py3-none-any.whl (20.4 kB view details)

Uploaded Python 3

File details

Details for the file celparser-0.1.2.tar.gz.

File metadata

  • Download URL: celparser-0.1.2.tar.gz
  • Upload date:
  • Size: 20.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.6.12

File hashes

Hashes for celparser-0.1.2.tar.gz
Algorithm Hash digest
SHA256 e91715b8a1f1b162cdb04dd4b1c618666427eb308803b855ff70107c22c305c0
MD5 0b22aee957e6f0985ca66dffb0346bb3
BLAKE2b-256 2616d29ebee431217ff058423c6f511aa936477a4ab64e7747c15e2a2a877867

See more details on using hashes here.

File details

Details for the file celparser-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: celparser-0.1.2-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.2-py3-none-any.whl
Algorithm Hash digest
SHA256 23d6363218132b8da863b20c2c2eb818dae54fdf16d394167dd38f39c7113e0b
MD5 ed2ff40e53735bd5688106a8bc6df073
BLAKE2b-256 0f97af15f34126e5948f575c5eba3b7aec1b31e55865f2af629c2175cce3fc25

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