Skip to content

Commit 4b4c5d6

Browse files
committed
Adding PyDF (Python Display Filters) matcher.
1 parent fb93b0c commit 4b4c5d6

File tree

4 files changed

+47
-2
lines changed

4 files changed

+47
-2
lines changed

c8y_api/model/matcher/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,14 @@
3838
'command',
3939
]
4040

41+
try:
42+
import pydictdisplayfilter as _pydictdisplayfilter
43+
from ._pydf_matcher import PydfMatcher, pydf
44+
__all__.append('PydfMatcher')
45+
__all__.append('pydf')
46+
except ImportError:
47+
pass
48+
4149
try:
4250
import jmespath as _jmespath
4351
from ._jmespath_matcher import JmesPathMatcher, jmespath
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Copyright (c) 2025 Cumulocity GmbH
2+
3+
from pydictdisplayfilter.display_filters import DictDisplayFilter
4+
5+
from c8y_api.model.matcher._matcher import JsonMatcher
6+
7+
8+
class PydfMatcher(JsonMatcher):
9+
"""JsonMatcher implementation for JMESPath."""
10+
11+
def __init__(self, expression: str, warn_on_error: bool = True):
12+
super().__init__(expression, warn_on_error)
13+
self.display_filter = DictDisplayFilter([])
14+
self.compiled_expression = self.display_filter._display_filter_parser.parse(expression)
15+
16+
def matches(self, json: dict) -> bool:
17+
# pylint: disable=broad-exception-caught
18+
return self.display_filter._evaluate_expressions(self.compiled_expression, json)
19+
20+
21+
def pydf(expression: str) -> PydfMatcher:
22+
"""Create a PydfMatcher from an expression."""
23+
return PydfMatcher(expression)

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,10 @@ dependencies = [
3434
]
3535

3636
[project.optional-dependencies]
37+
pydf = ["python-dict-display-filter"]
3738
jmespath = ["jmespath"]
3839
jsonpath = ["jsonpath-ng"]
39-
filters = ["jmespath", "jsonpath-ng"]
40+
filters = ["python-dict-display-filter", "jmespath", "jsonpath-ng"]
4041

4142
[project.urls]
4243
Homepage = "https://github.com/Cumulocity-IoT/cumulocity-python-api"

tests/model/test_matchers.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,20 @@ def test_command_matcher():
169169
matches_mock.assert_not_called()
170170

171171

172+
def test_pydf_matcher():
173+
"""Verify that the pydf matchers work as expected."""
174+
assert pydf("name == NAME").matches({'name': 'NAME'})
175+
assert pydf("name[0:2] eq NA").matches({'name': 'NAME'})
176+
assert pydf("name contains AM").matches({'name': 'NAME'})
177+
assert pydf("name in {'NAME', 'NONAME'}").matches({'name': 'NAME'})
178+
assert pydf("lower(name) == name").matches({'name': 'NAME'})
179+
assert pydf("len(name) == 4").matches({'name': 'NAME'})
180+
assert not pydf("name == 'NAME'").matches({'name': 'RANDOM'})
181+
with pytest.raises(Exception) as error:
182+
pydf("*INVALID*").matches({})
183+
assert "Error parsing display filter" in str(error.value)
184+
185+
172186
def test_jmespath_matcher():
173187
"""Verify that the jmespath matchers work as expected."""
174188
assert jmespath("name == 'NAME'").matches({'name': 'NAME'})
@@ -177,7 +191,6 @@ def test_jmespath_matcher():
177191
jmespath("*INVALID*").matches({})
178192
assert "INVALID" in str(error.value)
179193

180-
# $[?(@.firstName == "John")]
181194

182195
def test_jsonpath_matcher():
183196
"""Verify that the jsonpath matchers work as expected."""

0 commit comments

Comments
 (0)