Predicates
Predicates are first-class Python objects that express conditions over a fact's
fields. They compose with & | ~ and serialize cleanly.
Basic usage
from airules import Predicate
p = Light.color.eq("yellow") & Light.remaining_time.gt(5)
p.evaluate(Light(color="yellow", remaining_time=10)) # True
p(Light(color="yellow", remaining_time=2)) # False - same thing
# Compose with &, |, ~
either = Light.color.eq("red") | Light.color.eq("yellow")
not_green = ~Light.color.eq("green")
Available operators
| Class | Method shorthand | Description |
|---|---|---|
Eq | .eq(value) | Equality |
Gt | .gt(value) | Greater than |
Ge | .ge(value) | Greater than or equal |
Lt | .lt(value) | Less than |
Le | .le(value) | Less than or equal |
Contains | .contains(value) | Substring or list membership |
StartsWith | .startswith(prefix) | String prefix |
EndsWith | .endswith(suffix) | String suffix |
And | p & q | Boolean AND |
Or | p | q | Boolean OR |
Not | ~p | Boolean NOT |
Always | - | Always matches (used internally by @Default) |
Case-insensitive matching
eq, startswith, endswith, and contains accept a keyword-only
case_insensitive=True flag:
User.name.eq("alice", case_insensitive=True) # matches "Alice", "ALICE"
User.name.startswith("dr.", case_insensitive=True) # matches "Dr. Strange"
User.name.contains("smith", case_insensitive=True) # matches "John SMITH"
Notes:
eqignores case only when both values are strings; numbers,None, and enums compare exactly.containsfolds case for substring matches only - list/set membership stays exact.- The flag survives
to_dict()/from_dict()round-trips; it is omitted from the dict whenFalseso older serialized predicates load unchanged.
Serialization
Predicates round-trip through plain dicts:
data = p.to_dict()
restored = Predicate.from_dict(data)
Store them in a database, diff them between deployments, or feed them into a rules-editor UI. See Introspection for dumping the full rule set.