Commit 3b877f08 authored by Michael Hanselmann's avatar Michael Hanselmann

query: Add operator for truth

The “?” operator is the equivalent of “if var” in Python.
Signed-off-by: default avatarMichael Hanselmann <hansmi@google.com>
Reviewed-by: default avatarIustin Pop <iustin@google.com>
parent a84d1115
......@@ -31,6 +31,7 @@ OP_AND = "&"
# Unary operators
OP_NOT = "!"
OP_TRUE = "?"
# Binary operators
......
......@@ -357,8 +357,7 @@ class _FilterCompilerHelper:
- C{_OPTYPE_LOGIC}: Callable taking any number of arguments; used by
L{_HandleLogicOp}
- C{_OPTYPE_UNARY}: Callable taking exactly one parameter; used by
L{_HandleUnaryOp}
- C{_OPTYPE_UNARY}: Always C{None}; details handled by L{_HandleUnaryOp}
- C{_OPTYPE_BINARY}: Callable taking exactly two parameters, the left- and
right-hand side of the operator, used by L{_HandleBinaryOp}
......@@ -369,7 +368,8 @@ class _FilterCompilerHelper:
qlang.OP_AND: (_OPTYPE_LOGIC, compat.all),
# Unary operators
qlang.OP_NOT: (_OPTYPE_UNARY, operator.not_),
qlang.OP_NOT: (_OPTYPE_UNARY, None),
qlang.OP_TRUE: (_OPTYPE_UNARY, None),
# Binary operators
qlang.OP_EQUAL: (_OPTYPE_BINARY, _EQUALITY_CHECKS),
......@@ -449,6 +449,15 @@ class _FilterCompilerHelper:
return handler(hints_cb, level, op, op_data, operands)
def _LookupField(self, name):
"""Returns a field definition by name.
"""
try:
return self._fields[name]
except KeyError:
raise errors.ParameterError("Unknown field '%s'" % name)
def _HandleLogicOp(self, hints_fn, level, op, op_fn, operands):
"""Handles logic operators.
......@@ -485,6 +494,8 @@ class _FilterCompilerHelper:
@param operands: List of operands
"""
assert op_fn is None
if hints_fn:
hints_fn(op)
......@@ -492,8 +503,18 @@ class _FilterCompilerHelper:
raise errors.ParameterError("Unary operator '%s' expects exactly one"
" operand" % op)
return compat.partial(_WrapUnaryOp, op_fn,
self._Compile(operands[0], level + 1))
if op == qlang.OP_TRUE:
(_, _, _, retrieval_fn) = self._LookupField(operands[0])
op_fn = operator.truth
arg = retrieval_fn
elif op == qlang.OP_NOT:
op_fn = operator.not_
arg = self._Compile(operands[0], level + 1)
else:
raise errors.ProgrammerError("Can't handle operator '%s'" % op)
return compat.partial(_WrapUnaryOp, op_fn, arg)
def _HandleBinaryOp(self, hints_fn, level, op, op_data, operands):
"""Handles binary operators.
......@@ -516,10 +537,7 @@ class _FilterCompilerHelper:
raise errors.ParameterError("Invalid binary operator, expected exactly"
" two operands")
try:
(fdef, datakind, field_flags, retrieval_fn) = self._fields[name]
except KeyError:
raise errors.ParameterError("Unknown field '%s'" % name)
(fdef, datakind, field_flags, retrieval_fn) = self._LookupField(name)
assert fdef.kind != QFT_UNKNOWN
......
......@@ -1317,6 +1317,7 @@ class TestQueryFilter(unittest.TestCase):
{ "name": "node2", "other": ["x", "y", "bar"], },
{ "name": "node3", "other": "Hello", },
{ "name": "node1", "other": ["a", "b", "foo"], },
{ "name": "empty", "other": []},
]
q = query.Query(fielddefs, ["name", "other"], namefield="name",
......@@ -1343,6 +1344,21 @@ class TestQueryFilter(unittest.TestCase):
["node2", ["x", "y", "bar"]],
])
# Boolean test
q = query.Query(fielddefs, ["name", "other"], namefield="name",
filter_=["?", "other"])
self.assertEqual(q.OldStyleQuery(data), [
["node1", ["a", "b", "foo"]],
["node2", ["x", "y", "bar"]],
["node3", "Hello"],
])
q = query.Query(fielddefs, ["name", "other"], namefield="name",
filter_=["!", ["?", "other"]])
self.assertEqual(q.OldStyleQuery(data), [
["empty", []],
])
def testFilterHostname(self):
fielddefs = query._PrepareFieldList([
(query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
......@@ -1402,6 +1418,73 @@ class TestQueryFilter(unittest.TestCase):
["node2.example.net"],
])
def testFilterBoolean(self):
fielddefs = query._PrepareFieldList([
(query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
(query._MakeField("value", "Value", constants.QFT_BOOL, "Value"),
None, 0, lambda ctx, item: item["value"]),
], [])
data = [
{ "name": "node1", "value": False, },
{ "name": "node2", "value": True, },
{ "name": "node3", "value": True, },
]
q = query.Query(fielddefs, ["name", "value"],
filter_=["|", ["=", "value", False],
["=", "value", True]])
self.assertTrue(q.RequestedNames() is None)
self.assertEqual(q.Query(data), [
[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
[(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
[(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
])
q = query.Query(fielddefs, ["name", "value"],
filter_=["|", ["=", "value", False],
["!", ["=", "value", False]]])
self.assertTrue(q.RequestedNames() is None)
self.assertEqual(q.Query(data), [
[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
[(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
[(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
])
# Comparing bool with string
for i in ["False", "True", "0", "1", "no", "yes", "N", "Y"]:
self.assertRaises(errors.ParameterError, query.Query,
fielddefs, ["name", "value"],
filter_=["=", "value", i])
# Truth filter
q = query.Query(fielddefs, ["name", "value"], filter_=["?", "value"])
self.assertTrue(q.RequestedNames() is None)
self.assertEqual(q.Query(data), [
[(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
[(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
])
# Negative bool filter
q = query.Query(fielddefs, ["name", "value"], filter_=["!", ["?", "value"]])
self.assertTrue(q.RequestedNames() is None)
self.assertEqual(q.Query(data), [
[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
])
# Complex truth filter
q = query.Query(fielddefs, ["name", "value"],
filter_=["|", ["&", ["=", "name", "node1"],
["!", ["?", "value"]]],
["?", "value"]])
self.assertTrue(q.RequestedNames() is None)
self.assertEqual(q.Query(data), [
[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
[(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
[(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
])
if __name__ == "__main__":
testutils.GanetiTestProgram()
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment