A meta-language to evaluate boolean expressions using a dict's keys as context variables.
Booley parses a small, human-readable expression language — things like
age >= 18 AND country == "US" — and evaluates it against a Python dict
that supplies the values of the variables referenced in the expression.
pip install booleyRequires pyparsing (installed automatically).
from booley import Booley
context = {
'first_name': 'Michael',
'last_name': 'Jordan',
'age': 60,
}
parser = Booley(context)
parser.parse('first_name == "Michael" AND age >= 18')
# TrueYou can also reuse the same parser with a different context on every call:
parser = Booley()
parser.parse('age >= 18', {'age': 21}) # True
parser.parse('age >= 18', {'age': 15}) # Falsecheck_syntax validates that an expression is well-formed and evaluates to a
boolean, without needing real values for the variables:
parser.check_syntax('age >= 18 AND country == "US"') # True
parser.check_syntax('age >=') # raises BooleySyntaxError| Type | Examples |
|---|---|
| String | 'single quoted', "double quoted" |
| Integer | 9111, +9111, -9111 |
| Real | 1.0, .123, -1.12355 |
| Boolean | TRUE, FALSE (case-insensitive) |
| Variable | Any key present in the context dict, e.g. age, first_name |
Variables are resolved from the context dict passed to parse() / check_syntax().
A missing variable resolves to an empty string. List values are joined with |
before being compared as strings.
Variables can be sliced with name[start:end] (Python-style slicing on the
resolved string value):
parser.parse('alpha[2:7] == "cdefg"', {'alpha': 'abcdefghijklmnopqrstuvwxyz'}) # True| Operator | Meaning | Works on |
|---|---|---|
=, == |
Equal | numbers, strings, booleans |
!= |
Not equal | numbers, strings, booleans |
<, <=, >, >= |
Ordering comparison | numbers, strings |
HAS |
Left contains right | strings/lists |
NOT HAS |
Left does not contain right | strings/lists |
IN |
Left is contained in right | strings/lists |
NOT IN |
Left is not contained in right | strings/lists |
STARTS WITH |
Left starts with right | strings |
NOT STARTS WITH |
Left does not start with right | strings |
ENDS WITH |
Left ends with right | strings |
NOT ENDS WITH |
Left does not end with right | strings |
IS NULL |
Left is None |
any |
IS NOT NULL |
Left is not None |
any |
LENGTH IS |
len(left) == right |
strings |
LENGTH IS NOT |
len(left) != right |
strings |
All keyword operators (HAS, IN, STARTS WITH, ENDS WITH, LENGTH IS, ...)
are case-insensitive.
| Operator | Meaning |
|---|---|
AND |
Logical and |
OR |
Logical or |
NOT |
Logical negation |
( ) |
Grouping / precedence |
Expressions can be nested and combined freely:
parser.parse(
"TRUE AND (2 > 1) AND NOT 1 == 1 OR 'a' != 'b' OR NOT ((TRUE OR (2 > 1)) == (height > 2))",
{'height': 1.98}
)context = {
'name': 'Michael Jordan',
'first_name': 'Michael',
'last_name': 'Jordan',
'weight': '98.0',
'height': '1.98',
'retired': True,
'fruits': ['apple', 'banana'],
}
parser = Booley(context)
parser.parse('first_name == "Michael"') # True
parser.parse('last_name != "Bryant"') # True
parser.parse('fruits HAS "apple"') # True
parser.parse('"apple" IN fruits') # True
parser.parse('name STARTS WITH "Michael"') # True
parser.parse('name ENDS WITH "Jordan"') # True
parser.parse('name LENGTH IS 14') # True
parser.parse('height > 2') # FalseBooley raises exceptions from booley.exceptions:
BooleySyntaxError— the expression could not be parsed, or an operator was used with an invalid operand (e.g.IS NOT NULLcompared against something other thanNULL).UnknownOperation— an internal/unsupported operator was encountered.VariableNotFound— reserved for variable resolution errors.
python -m unittest booley.testsMIT — see LICENSE.