From 3196d7f79327f8d7ebaf8270c960a8f161578877 Mon Sep 17 00:00:00 2001 From: Dmitri Plotnikov Date: Mon, 28 Sep 2026 12:31:13 -0700 Subject: [PATCH] [Python] Ensure returned cel::Value does not depend on cel::Program lifetime by calling Clone Clone the evaluated cel::Value onto the evaluation arena in PyCelExpression::Eval. This ensures that any constants or constant-folded values (such as strings or bytes) whose storage is borrowed from cel::Program are copied to the arena and can safely outlive the cel.Expression. PiperOrigin-RevId: 989777356 --- cel_expr_python/cel_test.py | 16 ++++++++++++++++ cel_expr_python/py_cel_expression.cc | 1 + 2 files changed, 17 insertions(+) diff --git a/cel_expr_python/cel_test.py b/cel_expr_python/cel_test.py index fd5c8ef..377dadd 100644 --- a/cel_expr_python/cel_test.py +++ b/cel_expr_python/cel_test.py @@ -885,6 +885,22 @@ def testProtoMessageToCelValueError(self): self.assertEqual(res.type(), cel.Type.ERROR) self.assertIn("Custom pool error", str(res.value())) + def testValueOutlivesExpression(self): + expr: cel.Expression = self.env.compile("'hello ' + 'world'") + val: cel.Value = expr.eval() + del expr + self.assertEqual(val.value(), "hello world") + + expr = self.env.compile("b'hello'") + val = expr.eval() + del expr + self.assertEqual(val.value(), b"hello") + + expr = self.env.compile("['a', 'b', 'c']") + val = expr.eval() + del expr + self.assertEqual(val.plain_value(), ["a", "b", "c"]) + class CompatibleNumber: diff --git a/cel_expr_python/py_cel_expression.cc b/cel_expr_python/py_cel_expression.cc index 41a645a..725c4ab 100644 --- a/cel_expr_python/py_cel_expression.cc +++ b/cel_expr_python/py_cel_expression.cc @@ -202,6 +202,7 @@ absl::StatusOr PyCelExpression::Eval( result, program->Evaluate(arena->GetArena(), *activation.GetActivation(), std::move(options))); + result = result.Clone(arena->GetArena()); } return PyCelValue(result, arena, std::move(env)); }