All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Upgrades to cel-rust 0.14, ships an extended standard library that mirrors
cel-go, and exposes expression static analysis. A 1.0 release is intentionally
deferred until the upstream cel crate supports the CEL protobuf AST
(cel.expr.Expr / CheckedExpr) so that cross-implementation portability can
be offered — see the Notes section below.
- Updated cel-rust from 0.13.0 to 0.14.0.
- Updated PyO3 from 0.27 to 0.29. This resolves two upstream advisories that
affected earlier PyO3 versions: an out-of-bounds read in
nth/nth_backforPyList/PyTupleiterators (high), and a missingSyncbound onPyCFunction::new_closureclosures (moderate). - Switched
pyo3-logfrom a git fork (pinned to a PyO3 0.27 branch) to the releasedpyo3-log0.13.4 crate, removing the git dependency. - Refreshed transitive dependencies (chrono 0.4.45, log 0.4.33, regex 1.12.4, serde_json 1.0.150, arc-swap 1.9.2).
- Extended standard library (
cel.stdlib): opt-in function libraries that mirror cel-go's extensions and fill gaps in cel-rust's built-ins. Register them withcel.stdlib.add_stdlib_to_context(context)(all libraries) or passextensions=[...]to select individual ones. The CLI enables them automatically. Libraries:core:bool,dyn,type,min,maxstrings:charAt,indexOf,lastIndexOf,substring,replace,split,join,lowerAscii,upperAscii,trim,reverse,strings.quotemath:math.greatest,math.least,math.abs,math.sign,math.ceil,math.floor,math.round,math.trunc,math.isNaN,math.isInf,math.isFinite,math.sqrt, and themath.bit*operationssets:sets.contains,sets.equivalent,sets.intersectsencoders:base64.encode,base64.decodelists:contains,distinct,flatten,slice,sort,reverse,first,last,lists.range
- Expression static analysis on
Program:variables(),functions()andreferences()report the identifiers an expression uses without evaluating it.Program.sourceexposes the original CEL text. - Method-call syntax for custom functions: a registered function can now be
called either as
f(x, y)or asx.f(y). When called as a method the receiver is passed as the first argument, matching CEL's semantics. This is what lets the standard-library extensions be written as members ("s".charAt(i),[1,2].contains(x)).
- Behaviour change (cel 0.14):
containsis now a string-only built-in.[1, 2, 3].contains(2)and{...}.contains(k)no longer resolve to a built-in — use theinoperator (2 in [1, 2, 3],"k" in m) for list/map membership, or enable thelistsextension incel.stdlibwhich restores a multi-typecontains. - Behaviour change (cel 0.14):
minandmaxare no longer part of the core standard library. They are provided by thecoreextension incel.stdlib. - Behaviour change (cel 0.14): built-in functions and overloads now take
precedence over user-registered functions of the same name (e.g. registering
a function called
doublewill not override the built-indouble()conversion). Give custom functions names that do not collide with built-ins. - Error messages: type-mismatch errors now report canonical CEL type names
(e.g.
Unsupported operation: string + int) and several additional execution errors map to idiomatic Python exceptions (UnsupportedIndex,ValuesNotComparable,UnexpectedType,InvalidArgumentCount→TypeError).
- The CEL standard-library environment is now built once and shared across all
evaluations (via cel 0.14's
Env/Context::with_env) instead of being rebuilt on everyevaluate()/Program.execute()call.
- Cross-implementation portability / "bytecode" is explicitly out of scope.
CEL has no bytecode format; portable interchange in the ecosystem uses the
protobuf AST (
cel.expr.Expr/CheckedExpr), which the upstreamcelRust crate cannot yet produce or consume (it has no protobuf support and no type checker). The portable artifact for this library is the CEL source string;Program.references()provides static analysis.
- Updated cel-rust from 0.12.0 to 0.13.0.
Context.set_variable_resolver(callback)exposes cel 0.13'sVariableResolvertrait to Python. The callback receives a variable name and returns the value (orNoneto fall through to variables registered withadd_variable). Useful for backing a CEL context with on-demand sources (database lookups, lazily-loaded config files, etc.) without materializing the full set of variables upfront. Exceptions raised by the resolver are logged and treated as "not handled".- Idiomatic Python exception mapping for several CEL runtime errors that previously fell through to
ValueError:- Arithmetic overflow →
OverflowError(e.g.9223372036854775807 + 1, including the new overflow-safe int math in cel 0.13). - Division by zero / modulo by zero →
ZeroDivisionError. - List index out of bounds →
IndexError. - Missing map key (e.g.
{"a": 1}.b) →KeyError.
- Arithmetic overflow →
- Behaviour change (cel 0.13): bytes concatenation with
+now works per the CEL spec (b'hello' + b'world'returnsb'helloworld'). Previously raisedTypeError. - Behaviour change (cel 0.13): logical
&&and||are now "err-resilient" per CEL spec —X && falseshort-circuits tofalseandX || trueshort-circuits totrueeven whenXis not a boolean. Conversely,false || Xandtrue && Xnow raiseTypeErrorwhenXis not boolean (previouslyfalse || XreturnedX). - Error mapping: more operations now route through CEL's
NoSuchOverload(e.g.1 + 2u,1 * 2u, indexing into a string). These map toTypeErrorwith a generic message listing common causes and conversion functions (int(x),uint(x),double(x)). The previous type-specific messages (e.g. "Cannot mix signed and unsigned integers") are still produced for the operand orderings cel-rust dispatches viaUnsupportedBinaryOperator. Tests asserting on specific message text may need updating. - Behaviour change (cel 0.13): no implicit type coercion on map index access; indexing into a string now raises
TypeError(NoSuchOverload) per CEL spec. - Behaviour change (cel 0.13): integer arithmetic overflow now raises
OverflowErrorinstead of silently wrapping. Affects+,-,*on bothintanduintat the type's bounds.
-
Microbenchmarks comparing cel 0.13 vs 0.12 (release build, taking the min of 3 runs per scenario):
Scenario compile compiled execute evaluate x + y * 2+2.8% +8.4% +3.3% greet + ' ' + name+23.0% +4.3% +0.5% size(items)(1k)~0% +63.4% +53.2% map field access −7.1% +8.3% +6.5% custom Python fn +1.4% +6.7% −1.9% Most scenarios are within ~10% of 0.12; the
size(items)regression on a 1000-element list is cel 0.13's dyn-Val refactor adding per-element boxing overhead at the Python→CEL boundary. Smaller lists are not noticeably affected.
- Mapping conversion now respects dict subclasses and custom
Mappingimplementations, preserving__getitem__behavior for member access (issue #22)
compile()API andProgram.execute()for reuse and performanceOptionalValuewrapper to expose CEL optional values in Python- Compile vs execute benchmark script (
examples/performance/compile_execute_benchmark.py)
- Updated cel-rust from 0.11.6 to 0.12.0
- Documentation: pre-compilation guidance and OptionalValue examples
- Updated cel-rust from 0.11.4 to 0.11.6
- Updated PyO3 from 0.25.1 to 0.27.1
- Reduced logging verbosity
- Added new
cel.stdlibmodule with Python implementations of CEL functions missing from upstream cel-rust. - CLI automatically includes all stdlib functions
- Minor removed warning level logging in cel crate
- Python evaluation mode removed: The library now operates exclusively in strict CEL mode
- Removed:
EvaluationMode.PYTHONand all automatic integer-to-float promotion - Removed:
modeparameter fromevaluate()function - Removed:
--modeCLI option - Behavior change: Mixed arithmetic like
1 + 2.5now raisesTypeErrorinstead of automatically promoting to3.5 - Migration: Use explicit type conversion (e.g.,
double(1) + 2.5) for mixed arithmetic - Rationale: Eliminates complex AST preprocessing that was breaking
has()short-circuiting and other CEL functions
- Removed:
- CEL function short-circuiting: Fixed issue where
has()and other CEL functions failed due to AST preprocessing interference - String literal corruption: Eliminated string literal modification that occurred during integer promotion preprocessing
- Updated cel crate from v0.11.0 to v0.11.1
- Updated documentation to reflect strict CEL mode operation
- Updated tests to work with strict CEL mode only
- Removed complex preprocessing logic
- EvaluationMode enum: Control type handling behavior in CEL expressions (deprecated and removed in later version)
EvaluationMode.PYTHON(default for Python API): Python-friendly type promotions (removed)EvaluationMode.STRICT(default for CLI): Strict CEL type rules with no coercion (now the only mode)
- Type checking support: Added complete type stub files (
.pyi) for PyO3 extension
- Upgraded
celcrate (formerlycel-interpreter) 0.10.0 → 0.11.0:- Function registration now uses
IntoFunctiontrait. - Python integration updated to use
Argumentsextractor for variadic args. - Imports renamed from
cel_interpreter::tocel::. - No changes to Python API – all existing code continues to work.
- Function registration now uses
- Internal: Refactored Python integration to match new CEL API.
- Updated dependencies:
- pyo3: 0.25.0 → 0.25.1
- pyo3-log: 0.12.1 → 0.12.4
- Prepared for upcoming CEL Rust features:
- Enhanced type system & introspection
type()function support- Optional value handling
- Automatic type coercion for mixed int/float arithmetic (deprecated and removed in later version):
- Float literals automatically promote integer literals to floats.
- Context variables containing floats trigger int → float promotion.
- Preserves array indexing with integers (e.g.,
list[2]stays integer).
- Enhanced error handling:
- Parser panics now caught with
std::panic::catch_unwind. - Invalid expressions return a
ValueErrorinstead of crashing Python.
- Parser panics now caught with
- Mixed-type arithmetic now works in expressions like:
3.14 * 22 + 3.14value * 2(wherevalueis float)
- Parser panics from
cel-interpreterhandled gracefully with proper error messages. - Updated to latest PyO3 APIs to remove deprecation warnings.
- Bytes concatenation (
b'hello' + b'world') unsupported in cel-interpreter 0.10.0.- Spec requires: should work.
- Current: returns
"Unsupported binary operator 'add'". - Workaround:
bytes(string(part1) + string(part2)). - Status: Missing feature in cel-interpreter, not in our wrapper.
- cel-interpreter: 0.9.0 → 0.10.0 (breaking changes internally)
- log: 0.4.22 → 0.4.27
- chrono: 0.4.38 → 0.4.41
- pyo3: 0.22.6 → 0.25.0 (major API upgrade to
IntoPyObject) - pyo3-log: 0.11.0 → 0.12.1 (compatible with PyO3 0.25.0)
- PyO3 migration: moved from deprecated
IntoPyto newIntoPyObjectAPI. - New conversion system improves error handling and type safety.
- All 120 tests pass on current dependency set.