Skip to main content

Composable, Pythonic business rules and validation engine with expression trees

Project description

rules-engine-py

CI Python License Version

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rules_engine_py-0.1.1.tar.gz (4.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

rules_engine_py-0.1.1-py3-none-any.whl (4.3 kB view details)

Uploaded Python 3

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

Hashes for rules_engine_py-0.1.1.tar.gz
Algorithm Hash digest
SHA256 d08dd1b3a7b203122466e8b7cb98350c504b754b711f48460d80dbd6ef86391a
MD5 d3a0676fdaaf8511d6eed276919cc091
BLAKE2b-256 e269be916787c774b3a5767c82661fed91cb54749b571345dc9f39a9d61dac6a

See more details on using hashes here.

File details

Details for the file rules_engine_py-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for rules_engine_py-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 5345ca9abaad779890fa401ac9ce553c03c19132241f29fbea47777a213dc923
MD5 928d3f3fb37f8c786b01aae640f7f8c4
BLAKE2b-256 2c9e95fda11b10c39d8548c0d0c31d8830e5c1b1264a6d6c72b951312cc22248

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page