Skip to content

Commit 79cbff5

Browse files
committed
Use re2 instead of re for match expressions
CEL specifies that match should use re2 pattern matching and not re semantics.
1 parent a27498e commit 79cbff5

File tree

4 files changed

+81
-5
lines changed

4 files changed

+81
-5
lines changed

poetry.lock

+62-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ python-dateutil = "^2.9.0.post0"
4444
pyyaml = "^6.0.1"
4545
types-pyyaml = "^6.0.12.20240311"
4646
types-python-dateutil = "^2.9.0.20240316"
47+
google-re2 = "^1.1.20240501"
4748

4849
[tool.poetry.group.dev.dependencies]
4950
behave = "^1.2.6"

src/celpy/evaluation.py

+11-2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import logging
4040
import operator
4141
import re
42+
import re2
4243
import sys
4344
from functools import reduce, wraps
4445
from typing import (Any, Callable, Dict, Iterable, Iterator, List, Mapping,
@@ -62,7 +63,6 @@
6263

6364
logger = logging.getLogger("evaluation")
6465

65-
6666
class CELSyntaxError(Exception):
6767
"""CEL Syntax error -- the AST did not have the expected structure."""
6868
def __init__(self, arg: Any, line: Optional[int] = None, column: Optional[int] = None) -> None:
@@ -293,6 +293,15 @@ def operator_in(item: Result, container: Result) -> Result:
293293
return result
294294

295295

296+
def function_matches(text: str, pattern: str):
297+
try:
298+
m = re2.search(pattern, text)
299+
except re2.error as ex:
300+
return CELEvalError("match error", ex.__class__, ex.args)
301+
302+
return celpy.celtypes.BoolType(m is not None)
303+
304+
296305
def function_size(container: Result) -> Result:
297306
"""
298307
The size() function applied to a Value. Delegate to Python's :py:func:`len`.
@@ -340,7 +349,7 @@ def function_size(container: Result) -> Result:
340349
# StringType methods
341350
"endsWith": lambda s, text: celpy.celtypes.BoolType(s.endswith(text)),
342351
"startsWith": lambda s, text: celpy.celtypes.BoolType(s.startswith(text)),
343-
"matches": lambda s, pattern: celpy.celtypes.BoolType(re.search(pattern, s) is not None),
352+
"matches": function_matches,
344353
"contains": lambda s, text: celpy.celtypes.BoolType(text in s),
345354
# TimestampType methods. Type details are redundant, but required because of the lambdas
346355
"getDate": lambda ts, tz_name=None: celpy.celtypes.IntType(ts.getDate(tz_name)),

tests/test_evaluation.py

+7
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,13 @@ def test_operator_in():
158158
assert isinstance(operator_in(celtypes.IntType(-1), container_2), CELEvalError)
159159

160160

161+
def test_function_matches():
162+
empty_string = celtypes.StringType("")
163+
# re2-specific patterns which behave differently than standard re
164+
assert function_matches(empty_string, "^\\z")
165+
assert isinstance(function_matches(empty_string, "^\\Z"), CELEvalError)
166+
167+
161168
def test_function_size():
162169
container_1 = celtypes.ListType([
163170
celtypes.IntType(42),

0 commit comments

Comments
 (0)