A lightweight library for building, evaluating, and translating JSON-based rules
Project description
JSON Rule Engine
A powerful and lightweight Python library for building, evaluating, and translating JSON-based business rules. Perfect for creating dynamic filtering systems, decision engines, and converting rules to Django ORM queries.
Key Features
- 🎯 Unified API: Single
RuleEngineclass for all functionality - 🔧 Configurable: No hardcoded domain assumptions - works for any use case
- 🐍 Pythonic: Intuitive
Field()andQ()builders for rule creation - ⚡ Zero Dependencies: Core functionality requires no external packages
- 🔌 Django Integration: Optional Django Q object conversion
- 📊 Extensible: Add custom operators and configurations
Installation
# Basic installation
pip install json-rule-engine
# With Django support
pip install json-rule-engine[django]
Quick Start
Initialize the Engine
from json_rule_engine import RuleEngine, Field, Q
# Create the engine - single entry point for all functionality
engine = RuleEngine()
Build Rules
# Using Field builder (recommended)
rule = Field('age').greater_than(18)
rule = Field('city').equals('NYC')
rule = Field('email').contains('@gmail.com')
# Combine rules with & (AND), | (OR), ~ (NOT)
rule = (
Field('city').equals('NYC') &
Field('age').gt(18) &
Field('tags').has_any(['premium', 'vip'])
)
# Using Django-style Q objects
rule = Q(city='NYC') & Q(age__gt=18)
Evaluate Rules
# Evaluate against dictionary
data = {'city': 'NYC', 'age': 25, 'tags': ['vip']}
result = engine.evaluate(rule, data) # True
# Check matches (returns bool)
matches = engine.matches(rule, data) # True
Django Integration
# Convert to Django Q object (requires Django)
q = engine.to_q(rule)
# Use in Django ORM
from myapp.models import Contact
contacts = Contact.objects.filter(q)
Advanced Usage
Custom Configuration
The engine is fully configurable - no hardcoded assumptions:
from json_rule_engine import RuleEngine, DependencyConfig
# Configure for your domain (e-commerce example)
config = DependencyConfig(
id_fields={
'categories': 'category_ids',
'products': 'product_ids',
'vendors': 'vendor_ids',
},
custom_field_pattern=r'^custom\.(\d+)\.(\w+)$'
)
# Initialize engine with custom config
engine = RuleEngine(dependency_config=config)
# Now your fields are recognized
rule = Field('categories').has_any([101, 102])
deps = engine.get_dependencies(rule)
print(deps.id_references['category_ids']) # {101, 102}
Working with Objects
from json_rule_engine import Evaluatable
class Customer(Evaluatable):
def __init__(self, name, age, city, tags=None):
self.name = name
self.age = age
self.city = city
self.tags = tags or []
def to_eval_dict(self):
return {
'name': self.name,
'age': self.age,
'city': self.city,
'tags': self.tags
}
# Create customers
customers = [
Customer('Alice', 25, 'NYC', ['vip']),
Customer('Bob', 17, 'LA', ['regular']),
Customer('Charlie', 30, 'NYC', ['premium'])
]
# Define rule
rule = Field('city').equals('NYC') & Field('age').gte(18)
# Batch evaluation
results = engine.batch(rule, customers)
print(f"Matches: {results['matches']}") # [Alice, Charlie]
print(f"Non-matches: {results['non_matches']}") # [Bob]
# Filter matching objects
nyc_adults = engine.filter(rule, customers) # [Alice, Charlie]
# Test with timing
result = engine.test(rule, customers[0])
print(f"Match: {result.matches}, Time: {result.eval_time_ms}ms")
Custom Operators
# Register custom operators
def between(values, data):
"""Check if value is between min and max."""
val, min_val, max_val = values
return min_val <= val <= max_val
engine.register_operator('between', between)
# Use custom operator
rule = {"between": [{"var": "age"}, 18, 65]}
result = engine.evaluate(rule, {"age": 25}) # True
Complex Nested Rules
# Build complex business logic
rule = RuleBuilder.and_(
RuleBuilder.field('status').equals('active'),
RuleBuilder.or_(
RuleBuilder.field('role').equals('admin'),
RuleBuilder.and_(
RuleBuilder.field('role').equals('user'),
RuleBuilder.field('permissions').has_any(['write', 'delete'])
)
),
RuleBuilder.not_(
RuleBuilder.field('banned').equals(True)
)
)
# Evaluate complex rule
data = {
'status': 'active',
'role': 'user',
'permissions': ['read', 'write'],
'banned': False
}
result = engine.evaluate(rule, data) # True
Working with JSON Rules
# Parse existing JSON rules
json_rule = {
"and": [
{"==": [{"var": "status"}, "active"]},
{"or": [
{">": [{"var": "credits"}, 100]},
{"==": [{"var": "plan"}, "premium"]}
]}
]
}
# Evaluate JSON directly
result = engine.evaluate(json_rule, data)
# Or wrap and combine with builder
from json_rule_engine import JsonRule
wrapped = JsonRule(json_rule)
combined = wrapped & Field('region').equals('US')
API Reference
RuleEngine Methods
The RuleEngine class is the main API entry point:
engine = RuleEngine(
dependency_config=None, # Optional: Custom dependency configuration
django_field_map=None # Optional: Custom Django field mappings
)
# Rule Evaluation
engine.evaluate(rule, data) # Evaluate rule against data
engine.matches(rule, data) # Returns bool
engine.test(rule, obj) # Test Evaluatable object with timing
engine.batch(rule, objects) # Evaluate multiple objects
engine.filter(rule, objects) # Filter matching objects
# Django Integration
engine.to_q(rule) # Convert to Django Q object
engine.to_q_with_explanation(rule) # Get Q object with explanation
engine.validate_json_rules(json) # Validate JSON structure
# Dependency Extraction
engine.get_dependencies(rule) # Extract field dependencies
# Configuration
engine.register_operator(name, func) # Add custom operator
engine.configure_dependencies(config) # Update dependency config
engine.configure_django_fields(map) # Update Django mappings
Rule Builders
# Field builder (recommended)
Field('name').equals(value)
Field('name').not_equals(value)
Field('name').greater_than(value)
Field('name').greater_or_equal(value)
Field('name').less_than(value)
Field('name').less_or_equal(value)
Field('name').contains(substring)
Field('name').startswith(prefix)
Field('name').endswith(suffix)
Field('name').is_empty()
Field('name').is_not_empty()
Field('name').has_any([values]) # For array fields
Field('name').has_all([values]) # Must have all
Field('name').has_none([values]) # Must have none
# Q objects (Django-style)
Q(field='value') # equals
Q(field__ne='value') # not equals
Q(field__gt=10) # greater than
Q(field__gte=10) # greater or equal
Q(field__lt=10) # less than
Q(field__lte=10) # less or equal
Q(field__contains='text') # contains
Q(field__has_any=[1,2]) # array operations
# Combining rules
rule1 & rule2 # AND
rule1 | rule2 # OR
~rule # NOT
# Helper functions
AND(rule1, rule2, rule3)
OR(rule1, rule2)
NOT(rule)
Supported Operators
Comparison
==(equals)!=(not equals)>(greater than)>=(greater or equal)<(less than)<=(less or equal)
String
contains- Substring checkstartswith- String prefixendswith- String suffixis_empty- Null or empty stringis_not_empty- Has value
Array/Set
has_any- Has at least one valuehas_all- Has all valueshas_none- Has none of the valuesis_in- Value is in list
Logic
&/AND- Logical AND|/OR- Logical OR~/NOT- Logical NOT
Migration from v1.x
Version 2.0 introduces a cleaner, unified API:
Old API (v1.x)
# Multiple confusing entry points
from json_rule_engine import evaluate, matches, to_q, json_to_q
result = evaluate(rule, data)
q = to_q(rule)
q = json_to_q(json_rules)
New API (v2.0)
# Single, clear entry point
from json_rule_engine import RuleEngine
engine = RuleEngine()
result = engine.evaluate(rule, data)
q = engine.to_q(rule)
Key Changes
- Unified API: All functionality through
RuleEngineclass - Configurable Dependencies: No more hardcoded field assumptions
- Cleaner Imports: Single entry point for discoverability
- Better Typing: Full type hints throughout
Examples
E-commerce Product Filtering
# Configure for e-commerce domain
config = DependencyConfig(
id_fields={
'categories': 'category_ids',
'brands': 'brand_ids',
'tags': 'tag_ids'
}
)
engine = RuleEngine(dependency_config=config)
# Build product filter rule
rule = (
Field('price').between(10, 100) &
Field('categories').has_any(['electronics', 'computers']) &
Field('in_stock').equals(True) &
(Field('rating').gte(4) | Field('featured').equals(True))
)
# Filter products
matching_products = engine.filter(rule, all_products)
User Permission System
# Define permission rule
can_edit = (
Field('role').equals('admin') |
(
Field('role').equals('editor') &
Field('departments').has_any(['content', 'marketing'])
)
)
# Check user permission
user_data = {
'role': 'editor',
'departments': ['content', 'design']
}
has_permission = engine.evaluate(can_edit, user_data) # True
Dynamic Form Validation
# Build validation rule from configuration
validation_rule = RuleBuilder.and_(
RuleBuilder.field('age').gte(18),
RuleBuilder.field('email').contains('@'),
RuleBuilder.or_(
RuleBuilder.field('phone').is_not_empty(),
RuleBuilder.field('email').is_not_empty()
)
)
# Validate form submission
form_data = {
'age': 25,
'email': 'user@example.com',
'phone': ''
}
is_valid = engine.matches(validation_rule, form_data) # True
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Support
If you encounter any problems or have questions, please open an issue on GitHub.
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 json_rule_engine-2.0.0.tar.gz.
File metadata
- Download URL: json_rule_engine-2.0.0.tar.gz
- Upload date:
- Size: 32.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9068de9c7899df3cd60e2afa9892b0f51114931d4760e6f39210b5a420c16a8e
|
|
| MD5 |
8e7c2d6c71eba6033b993a52b1d5db5c
|
|
| BLAKE2b-256 |
55eced14f57f1e13513c5afc2105b1a0e70805679b8a672d70463575347ba8e8
|
File details
Details for the file json_rule_engine-2.0.0-py3-none-any.whl.
File metadata
- Download URL: json_rule_engine-2.0.0-py3-none-any.whl
- Upload date:
- Size: 23.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fde92798aa60b72525f1e3f7c61eb97a4f7b94133fd5f7227bd27f171f06bbe6
|
|
| MD5 |
5644fc77d8f990d3c5f3112eb438a959
|
|
| BLAKE2b-256 |
8c46fabdf073ab6f335b8b6c8abfd104b2a58016986aef1afcd7fc0114cde806
|