diff --git a/karton/core/query.py b/karton/core/query.py index 253f8e1..bf9e0e9 100644 --- a/karton/core/query.py +++ b/karton/core/query.py @@ -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""" @@ -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: @@ -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 @@ -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) @@ -346,5 +357,6 @@ def convert(filters): {"$not": {"$or": negative_filter}}, {"$or": regular_filter}, ] - } + }, + _type_coercion=True, ) diff --git a/tests/test_task_filters.py b/tests/test_task_filters.py index 0a72287..9bc0870 100644 --- a/tests/test_task_filters.py +++ b/tests/test_task_filters.py @@ -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 = [ {