Composable, Pythonic business rules and validation engine with expression trees
Project description
rules-engine-py
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.
✨ Features
- 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
🚀 Quick Start
Installation
pip install rules-engine-py
Simple Example
from 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)) # True
Working with Nested Data
user = {
"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)) # True
🧠 Core Concepts
Field — The Main DSL Builder
Field 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
Supported Operations on Field
| 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 |
Logical Composition
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 = ~admin
🔍 Predicates
Predicates are used primarily with .any() and .all() on collections.
Built-in Predicates
from rules_engine.predicates import (
Equals, NotEquals,
GreaterThan, GreaterThanOrEqual,
LessThan, LessThanOrEqual,
StartsWith, EndsWith, Regex,
Contains, And, Or, Not
)
Examples
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)
💾 Serialization
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)
⚙️ Creating Custom Predicates
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.
📚 API Reference
Main Imports
from rules_engine import Field, Rule
from rules_engine.predicates import (
Equals, NotEquals, GreaterThan, GreaterThanOrEqual,
LessThan, LessThanOrEqual, StartsWith, EndsWith, Regex,
Contains, And, Or, Not
)
Key Classes
- 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()
🧩 Use Cases
- API request validation
- Feature flags and access control
- Dynamic data filtering
- Fraud detection systems
- Rule-based decision engines
- User segmentation and personalization
🤝 Contributing
Contributions, bug reports, and feature requests are welcome! See CONTRIBUTING.md for guidelines.
📄 License
MIT License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file rules_engine_py-0.1.1.tar.gz.
File metadata
- Download URL: rules_engine_py-0.1.1.tar.gz
- Upload date:
- Size: 4.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d08dd1b3a7b203122466e8b7cb98350c504b754b711f48460d80dbd6ef86391a
|
|
| MD5 |
d3a0676fdaaf8511d6eed276919cc091
|
|
| BLAKE2b-256 |
e269be916787c774b3a5767c82661fed91cb54749b571345dc9f39a9d61dac6a
|
File details
Details for the file rules_engine_py-0.1.1-py3-none-any.whl.
File metadata
- Download URL: rules_engine_py-0.1.1-py3-none-any.whl
- Upload date:
- Size: 4.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5345ca9abaad779890fa401ac9ce553c03c19132241f29fbea47777a213dc923
|
|
| MD5 |
928d3f3fb37f8c786b01aae640f7f8c4
|
|
| BLAKE2b-256 |
2c9e95fda11b10c39d8548c0d0c31d8830e5c1b1264a6d6c72b951312cc22248
|