Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix regression: missing string coercion for old-style negated filters #263

Merged
merged 1 commit into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 20 additions & 8 deletions karton/core/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,15 @@ def is_non_string_sequence(entry):
class Query(object):
"""The Query class is used to match an object against a MongoDB-like query"""

def __init__(self, definition):
def __init__(self, definition, _type_coercion=False):
"""
If _type_coercion is enabled: header values are coerced to string
when condition is also a string. It's implemented for compatibility
with old syntax e.g. {"execute": "!False"} filter vs {"execute": False}
header value.
"""
self._definition = definition
self._type_coercion = _type_coercion

def match(self, entry):
"""Matches the entry object against the query specified on instanciation"""
Expand All @@ -40,7 +47,7 @@ def _match(self, condition, entry):
)
if is_non_string_sequence(entry):
return condition in entry
return condition == entry
return self._eq(condition, entry)

def _extract(self, entry, path):
if not path:
Expand Down Expand Up @@ -108,9 +115,14 @@ def _not_implemented(*_):
def _noop(*_):
return True

@staticmethod
def _eq(condition, entry):
def _eq(self, condition, entry):
try:
if (
self._type_coercion
and type(condition) is str
and type(entry) is not str
):
return str(entry) == condition
return entry == condition
except TypeError:
return False
Expand Down Expand Up @@ -155,9 +167,8 @@ def _lte(condition, entry):
except TypeError:
return False

@staticmethod
def _ne(condition, entry):
return entry != condition
def _ne(self, condition, entry):
return not self._eq(condition, entry)

def _nin(self, condition, entry):
return not self._in(condition, entry)
Expand Down Expand Up @@ -346,5 +357,6 @@ def convert(filters):
{"$not": {"$or": negative_filter}},
{"$or": regular_filter},
]
}
},
_type_coercion=True,
)
33 changes: 33 additions & 0 deletions tests/test_task_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,39 @@ def test_non_string_headers(self):
})
self.assertFalse(task_different_value.matches_filters(filters))

def test_negated_non_string_headers(self):
filters = [
{
"block": True,
"execute": "!False"
}
]

task_non_block = Task(headers={
"block": False,
"execute": False,
})
self.assertFalse(task_non_block.matches_filters(filters))

task_non_block_execute = Task(headers={
"block": False,
"execute": True,
})
self.assertFalse(task_non_block_execute.matches_filters(filters))

task_block_non_execute = Task(headers={
"block": True,
"execute": False,
})
self.assertFalse(task_block_non_execute.matches_filters(filters))

task_block_execute = Task(headers={
"block": True,
"execute": True,
})
self.assertTrue(task_block_execute.matches_filters(filters))


def test_negated_filter_for_different_type(self):
filters = [
{
Expand Down
Loading