Source code for insights.parsr.query.boolean

The boolean module allows delayed evaluation of boolean expressions. You wrap
predicates in objects that have overloaded operators so they can be connected
symbolically to express ``and``, ``or``, and ``not``. This is useful if you
want to build up a complicated predicate and pass it to something else for
evaluation, in particular :py:class:`insights.parsr.query.Entry` instances.

    .. code-block:: python

        def is_even(n):
            return (n % 2) == 0

        def is_positive(n):
            return n > 0

        even_and_positive = pred(is_even) & pred(is_positive)

        even_and_positive(6) == True
        even_and_positive(-2) == False
        even_and_positive(3) == False

You can also convert two parameter functions to which you want to partially
apply an argument. The arguments partially applied will be those *after* the
first argument. The first argument is the value the function should evaluate
when it's fully applied.

    .. code-block:: python

        import operator
        lt = pred2(  # is lt(a, b) == (a < b)
        gt = pred2(  # is gt(a, b) == (a > b)

        gt_five = gt(5)  # creates a function of one argument that when called
                         # returns, 5)

        lt_ten = lt(10)  # creates a function of one argument that when called
                         # returns, 5)

        gt_five_and_lt_10 = gt(5) & lt(10)

from itertools import count
import six
import sys

[docs]class Boolean(object): def __and__(self, other): return All(self, other) def __or__(self, other): return Any(self, other) def __invert__(self): return Not(self)
[docs] def test(self, value): return True
def __call__(self, value): return self.test(value)
[docs] def to_pyfunc(self): ver = sys.version_info if ver[0] == 2 and ver[1] == 6: return self.test env = {} ids = count() def expr(b): if b is TRUE: return " True " elif b is FALSE: return " False " elif isinstance(b, All): return "(" + " and ".join(expr(p) for p in b.exprs) + ")" elif isinstance(b, Any): return "(" + " or ".join(expr(p) for p in b.exprs) + ")" elif isinstance(b, Not): return "(" + "not " + expr(b.query) + ")" elif isinstance(b, Predicate): num = next(ids) func = "func_{num}".format(num=num) args = "args_{num}".format(num=num) env[func] = b.func env[args] = b.args if isinstance(b, CaselessPredicate): return func + "(value.lower(), " + "*" + args + ")" return func + "(value, " + "*" + args + ")" else: raise Exception(b) func = """ def predicate(value): try: return {body} except Exception as ex: return False """.format(body=expr(self)) six.exec_(func, env, env) return env["predicate"]
class TRUE(Boolean): pass class FALSE(Boolean): def test(self, value): return False
[docs]class Any(Boolean): def __init__(self, *exprs): self.exprs = list(exprs)
[docs] def test(self, value): return any(q.test(value) for q in self.exprs)
[docs]class All(Boolean): def __init__(self, *exprs): self.exprs = list(exprs)
[docs] def test(self, value): return all(q.test(value) for q in self.exprs)
[docs]class Not(Boolean): def __init__(self, query): self.query = query
[docs] def test(self, value): return not self.query.test(value)
[docs]class Predicate(Boolean): def __init__(self, func, *args): self.func = func self.args = args
[docs] def test(self, value): try: return self.func(value, *self.args) except Exception: return False
[docs]class CaselessPredicate(Predicate):
[docs] def test(self, lhs): if isinstance(lhs, str): return super(CaselessPredicate, self).test(lhs.lower()) return super(CaselessPredicate, self).test(lhs)
[docs]def pred(func, ignore_case=False): if ignore_case: return CaselessPredicate(func) return Predicate(func)
[docs]def pred2(func, ignore_case=False): def inner(val): if ignore_case: return CaselessPredicate(func, val.lower()) return Predicate(func, val) return inner
Or = Any And = All TRUE = TRUE() FALSE = FALSE()