A composable, JSON-serializable rule engine for Python.
Build reusable, expressive business logic using a clean and intuitive DSL. Perfect for API validation, feature flags, access control, data filtering, fraud detection, and rule-based decision systems.
- Elegant DSL using
Field("path")with natural Python operators (>=,==,&,|,~) - Deep nested field access (
"user.profile.age","items.0.price") - Full JSON serialization & deserialization support
- Powerful collection operations:
.any()and.all() - Rich string, numeric, and length-based rules
- Logical composition with
&(AND),|(OR), and~(NOT) - Extensible via custom predicates
- Zero runtime dependencies
pip install rules-engine-pyfrom rules_engine import Field
# Build expressive rules using Field
rule = (
(Field("age") >= 18) & (Field("is_premium") == True)
) | (Field("role") == "admin")
data = {"age": 25, "is_premium": False, "role": "user"}
print(rule.evaluate(data)) # Trueuser = {
"profile": {"age": 25, "country": "ET"},
"roles": ["user", "editor"],
"bio": "Hello world from Ethiopia"
}
age_ok = Field("profile.age") >= 18
from_ethiopia = Field("profile.country") == "ET"
has_editor = Field("roles").contains("editor")
long_bio = Field("bio").len() > 10
rule = (age_ok & from_ethiopia) | (has_editor & long_bio)
print(rule.evaluate(user)) # TrueField is the primary way to create rules. It uses Python's operator overloading and method chaining to create readable rules.
from rules_engine import Field
# Simple comparisons
Field("age") >= 18
Field("status") == "active"
# String operations
Field("name").startswith("guest")
Field("email").matches(r".+@company\.com")
# Collection operations
Field("tags").contains("python")
Field("tags").len() >= 3| Operation | Example | Description |
|---|---|---|
==, !=, >, >=, <, <= |
Field("age") >= 18 |
Numeric / value comparison |
.startswith() |
Field("name").startswith("Ab") |
String prefix |
.endswith() |
Field("email").endswith(".com") |
String suffix |
.matches() |
Field("email").matches(r".+@example\\.com") |
Regex match |
.contains() |
Field("tags").contains("python") |
Check if value contains item |
.len() |
Field("tags").len() >= 3 |
Length comparison |
.any(predicate) |
Field("scores").any(GreaterThan(50)) |
Any item matches predicate |
.all(predicate) |
Field("tags").all(StartsWith("py")) |
All items match predicate |
You can combine rules using Python operators:
from rules_engine import Field
adult = Field("age") >= 18
premium = Field("is_premium") == True
admin = Field("role") == "admin"
# Combine rules
main_rule = (adult & premium) | admin
# Negation
not_admin = ~adminPredicates are used primarily with .any() and .all() on collections.
from rules_engine.predicates import (
Equals, NotEquals,
GreaterThan, GreaterThanOrEqual,
LessThan, LessThanOrEqual,
StartsWith, EndsWith, Regex,
Contains, And, Or, Not
)from rules_engine.predicates import Contains, GreaterThan, Equals
data = {
"tags": ["python", "ai", "ethiopia"],
"scores": [10, 20, 35]
}
has_ai_tag = Field("tags").any(Contains("ai"))
has_high_score = Field("scores").any(GreaterThan(30))
has_exact_two = Field("scores").any(Equals(2))You can also combine predicates logically:
complex_pred = And(StartsWith("py"), EndsWith("on"))
rule = Field("tags").any(complex_pred)All rules are fully serializable to JSON and can be restored later.
from rules_engine import Field, Rule
rule = (Field("age") >= 18) & Field("country") == "ET"
# Save to JSON
json_str = rule.to_json()
# Load from JSON
restored = Rule.from_json(json_str)
# Both rules behave identically
assert rule.evaluate(data) == restored.evaluate(data)Custom predicates can be created by subclassing Predicate and registering them:
from rules_engine.predicates.base import Predicate
@Predicate.register("is_even")
class IsEven(Predicate):
def evaluate(self, value):
if not isinstance(value, (int, float)):
return False
return value % 2 == 0
def to_dict(self):
return {"type": self._type}
@classmethod
def _from_dict_impl(cls, data):
return cls()
def __eq__(self, other):
return isinstance(other, IsEven)Then use it:
rule = Field("score").any(IsEven())Note: Custom Rules are currently not directly supported via the DSL. All rules must be created through the Field class.
from rules_engine import Field, Rule
from rules_engine.predicates import (
Equals, NotEquals, GreaterThan, GreaterThanOrEqual,
LessThan, LessThanOrEqual, StartsWith, EndsWith, Regex,
Contains, And, Or, Not
)- Field β DSL entry point for building rules
- Rule β Base class for all rules (used mainly for deserialization)
- Predicate β Base class for predicates used in
.any()/.all()
- API request validation
- Feature flags and access control
- Dynamic data filtering
- Fraud detection systems
- Rule-based decision engines
- User segmentation and personalization
Contributions, bug reports, and feature requests are welcome! See CONTRIBUTING.md for guidelines.
MIT License