Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/how-to-guides/cli-recipes.md
Original file line number Diff line number Diff line change
Expand Up @@ -548,8 +548,8 @@ cel 'users.map(u, {
"permissions_count": u.permissions.size()
})' --context-file users.json

# Filter and aggregate
cel 'products.filter(p, p.price > 100).map(p, p.price).fold(sum, 0, sum + item)' \
# Filter and extract prices
cel 'products.filter(p, p.price > 100).map(p, p.price)' \
--context-file products.json

# Nested filtering
Expand Down
42 changes: 27 additions & 15 deletions docs/reference/cel-compliance.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,21 @@ This document tracks the compliance of this Python CEL implementation with the [

- **Implementation**: Based on [`cel`](https://crates.io/crates/cel) v0.11.0 Rust crate (formerly cel-interpreter)
- **Estimated Compliance**: ~80% of CEL specification features.
- **Test Coverage**: 300+ tests across 15+ test files including comprehensive CLI testing and upstream improvement detection
- **Test Coverage**: 300+ tests across 16+ test files including comprehensive CLI testing and upstream improvement detection

## 🚨 Missing Features & Severity Overview

| **Feature** | **Severity** | **Impact** | **Workaround Available** | **Upstream Priority** |
|-------------|--------------|------------|--------------------------|----------------------|
| **OR operator behavior** | 🔴 **HIGH** | Returns original values instead of booleans | Use explicit boolean conversion | **CRITICAL** |
| **String utility functions** | 🟡 **MEDIUM** | Limited string processing capabilities | Use Python context functions | **HIGH** |
| **Type introspection (`type()`)** | 🟡 **MEDIUM** | No runtime type checking | Use Python type checking | **HIGH** |
| **Mixed int/uint arithmetic** | 🟡 **MEDIUM** | Manual type conversion needed | Use explicit casting | **MEDIUM** |
| **Mixed-type arithmetic in macros** | 🟡 **MEDIUM** | Type coercion issues in collections | Ensure type consistency | **MEDIUM** |
| **Bytes concatenation** | 🟢 **LOW** | Cannot concatenate byte arrays | Convert through string | **LOW** |
| **Math functions (`ceil`, `floor`)** | 🟢 **LOW** | No mathematical utilities | Use Python context functions | **LOW** |
| **Optional values** | 🟢 **LOW** | No optional chaining syntax | Use `has()` checks | **FUTURE** |
| **Feature** | **Severity** | **Impact** | **Workaround Available** | **Upstream Priority** |
|-----------------------------------------------------|--------------|------------|--------------------------|----------------------|
| **OR operator behavior** | 🔴 **HIGH** | Returns original values instead of booleans | Use explicit boolean conversion | **CRITICAL** |
| **String utility functions** | 🟡 **MEDIUM** | Limited string processing capabilities | Use Python context functions | **HIGH** |
| **Type introspection (`type()`)** | 🟡 **MEDIUM** | No runtime type checking | Use Python type checking | **HIGH** |
| **Mixed int/uint arithmetic** | 🟡 **MEDIUM** | Manual type conversion needed | Use explicit casting | **MEDIUM** |
| **Mixed-type arithmetic in macros** | 🟡 **MEDIUM** | Type coercion issues in collections | Ensure type consistency | **MEDIUM** |
| **Bytes concatenation** | 🟢 **LOW** | Cannot concatenate byte arrays | Convert through string | **LOW** |
| **Math functions (`ceil`, `floor`)** | 🟢 **LOW** | No mathematical utilities | Use Python context functions | **LOW** |
| **Collection aggregation (`sum`, `fold`, `reduce`)** | 🟢 **LOW** | No aggregation functions | Use Python context functions | **LOW** |
| **Optional values** | 🟢 **LOW** | No optional chaining syntax | Use `has()` checks | **FUTURE** |

**Legend**: 🔴 High Impact | 🟡 Medium Impact | 🟢 Low Impact

Expand Down Expand Up @@ -134,6 +135,7 @@ count + 1 // If count=5, stays as 5 + 1 → 6
| `matches()` | `string.matches(pattern) -> bool` | Regex matching | `bool` | ✅ Working |
| `min()` | `min(list) -> value` | Find minimum value | Various | ✅ Working |
| `max()` | `max(list) -> value` | Find maximum value | Various | ✅ Working |
| `sum()` | `sum(list) -> number` | Sum numeric values | N/A | ❌ **NOT AVAILABLE** |

### ✅ String Operations
- **contains()**: `"hello".contains("ell")` → `True`
Expand All @@ -150,6 +152,10 @@ count + 1 // If count=5, stays as 5 + 1 → 6
- **filter()**: `[1,2,3].filter(x, x > 1)` → `[2.0, 3.0]` (with type coercion)
- **map()**: Limited due to type system restrictions ⚠️ **PARTIAL** (requires type-compatible operations)

### ❌ Missing Collection Functions
- **fold()**: `[1,2,3].fold(0, sum, sum + x)` - Collection aggregation ❌ **NOT AVAILABLE**
- **reduce()**: `reduce([1,2,3], 0, sum + x)` - Reduction operations ❌ **NOT AVAILABLE**

### ✅ Python Integration
- **Automatic type conversion**: Seamless Python ↔ CEL type mapping
- **Context variables**: Access Python objects in expressions
Expand Down Expand Up @@ -350,6 +356,7 @@ This section covers upstream work, detection strategies, and contribution opport
- **Detection**: ✅ Full detection for all missing functions
**Missing functions**:
- Math: `ceil()`, `floor()`, `round()` - Mathematical functions
- Collection: `fold()`, `reduce()` - Collection aggregation functions
- Collection: Enhanced `in` operator behaviors
- URL/IP: `isURL()`, `isIP()` - Validation functions (available in some CEL implementations)

Expand Down Expand Up @@ -483,7 +490,7 @@ Both the CLI tool and the core `evaluate()` function now handle all malformed in
### 🎯 Upstream Contribution Priorities

#### High Priority (Ready for Contribution)
1. **String utility functions** - ✅ **Detection Ready**
1. **String utility functions** - ✅ **Detection Ready** (`test_upstream_detection.py`)
- Functions: `lowerAscii`, `upperAscii`, `indexOf`, `lastIndexOf`, `substring`, `replace`, `split`, `join`
- Impact: **MEDIUM** - Widely used in string processing applications
- Contribution path: cel crate standard library expansion
Expand All @@ -493,7 +500,7 @@ Both the CLI tool and the core `evaluate()` function now handle all malformed in
- Impact: **HIGH** - Breaks specification conformance
- Contribution path: Core logical operation fixes

3. **Type introspection function** - ✅ **Detection Ready**
3. **Type introspection function** - ✅ **Detection Ready** (`test_upstream_detection.py`)
- Function: `type()` for runtime type checking
- Impact: **MEDIUM** - Useful for dynamic expressions
- Contribution path: Leverage existing type system infrastructure
Expand All @@ -510,12 +517,17 @@ Both the CLI tool and the core `evaluate()` function now handle all malformed in
- Contribution path: Arithmetic type coercion enhancements

#### Low Priority (Future Features)
6. **Math functions** - ✅ **Detection Ready**
6. **Collection aggregation functions** - ✅ **Detection Ready**
- Functions: `sum()`, `fold()`, `reduce()`
- Impact: **LOW** - Can be implemented via Python context
- Contribution path: Standard library expansion

7. **Math functions** - ✅ **Detection Ready**
- Functions: `ceil`, `floor`, `round`
- Impact: **LOW** - Can be implemented via Python context
- Contribution path: Standard library expansion

7. **Optional value handling** - ✅ **Detection Ready**
8. **Optional value handling** - ✅ **Detection Ready**
- Features: `optional.of()`, `.orValue()`, `?` chaining
- Impact: **LOW** - Alternative patterns exist
- Contribution path: Type system extensions
Expand Down
1 change: 0 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -730,7 +730,6 @@ fn cel(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
pyo3_log::init();

m.add_function(wrap_pyfunction!(evaluate, m)?)?;

m.add_class::<context::Context>()?;
Ok(())
}
22 changes: 19 additions & 3 deletions tests/test_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,22 @@
import pytest


class TestBuiltInCollectionFunctions:
"""Test built-in collection functions that work in CEL."""

def test_min_function_works(self):
"""Test that min() function works correctly."""
assert cel.evaluate("min([3, 1, 4, 1, 5])") == 1
assert cel.evaluate("min([1.5, 2.3, 0.8])") == 0.8
assert cel.evaluate("min(['banana', 'apple', 'cherry'])") == "apple"

def test_max_function_works(self):
"""Test that max() function works correctly."""
assert cel.evaluate("max([3, 1, 4, 1, 5])") == 5
assert cel.evaluate("max([1.5, 2.3, 0.8])") == 2.3
assert cel.evaluate("max(['banana', 'apple', 'cherry'])") == "cherry"


def test_custom_function():
def custom_function(a, b):
return a + b
Expand Down Expand Up @@ -366,9 +382,9 @@ def simple_add(a, b):
end_time = time.perf_counter()
avg_time = (end_time - start_time) / iterations

# Should be reasonably fast (under 200 microseconds per call)
# Adjusted threshold for realistic hardware performance
assert avg_time < 0.0002, f"Function call too slow: {avg_time * 1000000:.1f} μs per call"
# Should be reasonably fast (under 300 microseconds per call)
# Adjusted threshold for realistic hardware performance and CI environments
assert avg_time < 0.0003, f"Function call too slow: {avg_time * 1000000:.1f} μs per call"

def test_complex_function_call_performance(self):
"""Test performance of more complex function calls."""
Expand Down
57 changes: 57 additions & 0 deletions tests/test_upstream_improvements.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,62 @@ def test_join_not_implemented(self):
cel.evaluate('["hello", "world"].join(",")')


class TestMissingAggregationFunctions:
"""Test aggregation functions that are missing from CEL."""

def test_sum_function_not_available(self):
"""
Test that sum() function is not currently available.

When this test starts failing, sum() has been implemented upstream.
"""
with pytest.raises(RuntimeError, match="Undefined variable or function.*sum"):
cel.evaluate("sum([1, 2, 3, 4, 5])")

def test_fold_function_not_available(self):
"""
Test that fold() is not available - various syntax attempts.

When this test starts failing, fold() has been implemented upstream.
"""
# Method syntax
with pytest.raises((RuntimeError, ValueError)):
cel.evaluate("[1, 2, 3, 4, 5].fold(0, (acc, x) -> acc + x)")

# Global function syntax
with pytest.raises(RuntimeError, match="Undefined variable or function.*fold"):
cel.evaluate("fold([1, 2, 3], 0, sum + x)")

def test_reduce_function_not_available(self):
"""
Test that reduce() is not available - various syntax attempts.

When this test starts failing, reduce() has been implemented upstream.
"""
# Global function syntax
with pytest.raises(RuntimeError, match="Undefined variable or function.*reduce"):
cel.evaluate("reduce([1, 2, 3, 4, 5], 0, sum + x)")

# Method syntax
with pytest.raises((RuntimeError, ValueError)):
cel.evaluate("[1, 2, 3].reduce(0, (acc, x) -> acc + x)")

@pytest.mark.xfail(reason="Aggregation functions not implemented in cel v0.11.0", strict=False)
def test_aggregation_functions_expected_behavior(self):
"""
Test expected aggregation function behavior when implemented.

This test will pass when upstream implements sum(), fold(), reduce().
"""
# Sum function
assert cel.evaluate("sum([1, 2, 3, 4, 5])") == 15
assert cel.evaluate("sum([1.1, 2.2, 3.3])") == pytest.approx(6.6)

# Fold/reduce functions (syntax may differ when actually implemented)
assert cel.evaluate("[1, 2, 3, 4].fold(0, (acc, x) -> acc + x)") == 10
assert cel.evaluate("[1, 2, 3].fold(1, (acc, x) -> acc * x)") == 6


class TestMathFunctions:
"""Test missing mathematical functions."""

Expand Down Expand Up @@ -316,6 +372,7 @@ def test_upstream_improvements_summary():
upstream improvements we're monitoring.
"""
improvements_to_watch = {
"Missing aggregation functions": ["sum()", "fold()", "reduce()"],
"String functions": [
"lowerAscii",
"upperAscii",
Expand Down
Loading