Skip to content

Add sum() to the extended stdlib, refresh dependencies and modernise CI - #39

Merged
hardbyte merged 4 commits into
mainfrom
claude/review-issues-prs-deps-xnrrmr
Aug 19, 2026
Merged

Add sum() to the extended stdlib, refresh dependencies and modernise CI#39
hardbyte merged 4 commits into
mainfrom
claude/review-issues-prs-deps-xnrrmr

Conversation

@hardbyte

Copy link
Copy Markdown
Owner

Closes #14.

Three related pieces of work: finishing the aggregation functions, refreshing dependencies, and making CI reproducible. Happy to split if you'd rather review them separately.

sum()closes #14

cel.stdlib's core library now provides sum alongside min/max, following Kubernetes' CEL list library (the implementation the issue asked to match):

ctx = cel.Context()
add_stdlib_to_context(ctx)

cel.evaluate("sum([1, 2, 3])", ctx)                      # 6
cel.evaluate("sum(1, 2, 3.5)", ctx)                      # 6.5
cel.evaluate("items.map(i, i.weight).sum() == 1.0", ctx)  # True
cel.evaluate('sum([duration("1h"), duration("30m")])', ctx)

All numeric types plus duration; sum([]) is 0; booleans are rejected rather than counted as 1/0; numbers and durations cannot be mixed. Both call forms work, since CEL treats x.f() as f(x).

Why fold/reduce are not in this PR

They cannot be implemented in this wrapper:

  • A CEL function receives its arguments already evaluated — in cel 0.14, FunctionContext.args is Vec<Cow<dyn Val>>. A fold needs the accumulator expression left unevaluated and re-bound for each element.
  • The comprehension macros that do that (has, all, exists, existsOne, map, filter) are expanded by the parser from a fixed match in parser/macros.rs, which isn't extensible from outside the crate.

So fold/reduce have to arrive in cel-rust. This is now documented in the cel.stdlib docstring and in the standard-library reference, and the xfail tracker for it is kept so we notice if upstream adds it.

Upstream check (the "implement it upstream instead" question)

Checked against cel-rust main at v0.14.3 and cel-go, and the answer differs per function:

  • Aggregations are not coming upstream. cel-rust 0.14.0 deliberately went the other way — its changelog has "(overloads) No more min or max by default" — and cel-go's math/lists extensions have greatest/least but no sum. None of min/max/sum are in the CEL spec; they're extension-library functions everywhere they exist, which is exactly what cel.stdlib is. cel-rust's own docs even use pub fn sum(Arguments(args): Arguments) as the example of a user-defined variadic function.
  • fold/reduce are upstream-only, for the reasons above.

Dependencies

  • cargo update inside the existing semver ranges: cel 0.14.0 → 0.14.3, PyO3 0.29.0 → 0.29.2, plus regex 1.13.1, serde 1.0.229, serde_json 1.0.151, thiserror 2.0.20, uuid 1.24.1.
  • No security advisories: all 81 locked crates checked against OSV.
  • uv.lock is now committed (removed from .gitignore) so CI resolves the same Python dependencies as local development.

CI

  • Fixes a latent lint failure on main. Ruff 0.16 began formatting Python code blocks inside Markdown, so with today's ruff ruff format --check . fails on 14 README/docs files even with no Python change — the next push to main would have failed the lint job purely from the unpinned ruff>=0.12.7. Markdown is now excluded from the formatter (docs snippets are still executed by tests/test_docs.py) and ruff is pinned to the 0.16 line.
  • Test matrix adds Python 3.13 and 3.14. Verified locally: the extension builds under PyO3 0.29 and the full suite passes on both.
  • Security workflow runs uvx safety scansafety check is retired, and uvx keeps the scanner out of this project's dev dependencies instead of mutating pyproject.toml mid-run.
  • Claude workflow moves from the deprecated @beta tag to anthropics/claude-code-action@v1.
  • tests/test_docs.py passes a sorted list rather than a generator to pytest.mark.parametrize — deprecated, and a hard error in pytest 10.
  • The upstream-watch xfail reasons still cited cel 0.11; they now name 0.14.3 and distinguish real gaps from CEL-spec-mandated behaviour (no implicit numeric coercion).

Verification

cargo fmt --check, cargo clippy --all-targets --all-features -D warnings, cargo test, ruff format --check, ruff check, mypy python/cel, uvx ty check, and the full pytest suite (472 passed, 1 skipped, 6 xfailed) — all clean, on Python 3.11, 3.13 and 3.14.


Generated by Claude Code

claude added 3 commits August 19, 2026 09:31
cargo update within the existing semver ranges: cel 0.14.0 -> 0.14.3,
pyo3 0.29.0 -> 0.29.2, plus transitive bumps (regex 1.13, serde 1.0.229,
thiserror 2.0, uuid 1.24). Rust tests, clippy, the Python suite (463
tests) and mypy/ty all pass on the refreshed lockfile.

Ruff 0.16 started formatting Python code blocks inside Markdown, which
made `ruff format --check .` fail on 14 docs/README files even though no
Python source changed — the next push to main would have failed the lint
job purely from the unpinned `ruff>=0.12.7`. Exclude Markdown from the
formatter (doc snippets are still executed by tests/test_docs.py) and
pin ruff to the 0.16 line so formatting is reproducible.

Also pass a sorted list rather than a generator to
`pytest.mark.parametrize` in tests/test_docs.py: passing a non-Collection
iterable is deprecated and becomes an error in pytest 10.
Closes the aggregation half of #14. `cel.stdlib`'s `core` library now provides
`sum` alongside `min`/`max`, following Kubernetes' CEL list library: all numeric
types plus `duration`, `sum([])` == 0, booleans rejected rather than counted as
1/0, and no mixing of numbers with durations. Both call forms work, so
`items.map(i, i.weight).sum()` reads the way the issue asked for.

`fold`/`reduce` cannot be implemented here. A CEL function receives evaluated
arguments, whereas a fold needs its accumulator expression left unevaluated and
re-bound per element, and cel 0.14's comprehension macros are expanded by the
parser from a fixed table (has/all/exists/existsOne/map/filter) that is not
extensible from outside the crate. That is now stated in the stdlib docstring
and the standard-library reference, with the upstream pointer.

Upstream direction checked while doing this: cel-rust 0.14.0 deliberately
dropped `min`/`max` from its default overloads, cel-go keeps only
`math.greatest`/`math.least`, and neither has `sum`, `fold` or `reduce` — so
aggregations belong in this wrapper's opt-in stdlib, while fold/reduce stay an
upstream feature request.

Also refreshed the upstream-watch trackers, which still cited cel 0.11: the
aggregation xfail is split into a fold/reduce tracker and a native-sum tracker,
and the remaining reasons now name 0.14.3 and say when the behaviour is CEL-spec
mandated rather than a gap.
- Stop ignoring uv.lock and commit it, so CI resolves the same Python
  dependencies as local development. Refresh with `uv lock --upgrade`.
- Test Python 3.13 and 3.14 in CI alongside 3.11 and 3.12. Verified locally:
  the extension builds under PyO3 0.29 and the full suite passes on both.
- Run the Python security scan as `uvx safety scan`: `safety check` is retired,
  and using uvx keeps the scanner out of this project's dev dependencies
  instead of mutating pyproject.toml mid-workflow.
- Move the Claude workflow from the deprecated `@beta` tag to
  `anthropics/claude-code-action@v1`.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 20a90139fe

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread python/cel/stdlib.py
Comment thread python/cel/stdlib.py
…k boundary

Both points raised in review are real in effect but are properties of the
Python-function boundary rather than of sum(): `min`, `max` and `math.abs`
already return an int for uint input, and `cel.evaluate('duration("1ns")')`
already yields timedelta(0) with no stdlib function involved. Python has a single
integer type and timedelta has microsecond resolution, so neither can be fixed
inside a Python callback — the honest fix is to stop implying otherwise.

The module docstring, the standard-library reference and sum()'s own docstring
now state that a uint sum returns an int (so `sum([1u, 2u]) + 1u` has no
overload, while the native `[1u, 2u][0] + 1u` works) and that sub-microsecond
durations are rounded before a function sees them, and both behaviours are
pinned by tests so a future change is deliberate.
@hardbyte
hardbyte merged commit 48a2f21 into main Aug 19, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support for fold or reduce?

2 participants