Business Rules
Business Rules is a Python Domain Specific Language (DSL) that allows you to define business intelligence rules without writing direct code. It provides a framework for defining 'variables' (data points) and 'actions' (operations) and then executing JSON-defined rules against them. The current stable version is 1.1.1, with releases occurring infrequently for maintenance and compatibility updates.
Common errors
-
AttributeError: 'SomeVariables' object has no attribute 'some_object'
cause The `__init__` method of your `BaseVariables` or `BaseActions` subclass expects an object (e.g., `some_object`) but it was not correctly passed during instantiation in `run_all`, or a `rule_variable`/`rule_action` method attempts to access an attribute that doesn't exist on the provided object.fixEnsure the object is correctly passed to the `defined_variables` and `defined_actions` parameters of `run_all`. Also, verify that attributes accessed within `rule_variable` and `rule_action` methods (e.g., `self.some_object.some_value`) actually exist on the object instance. -
No operator found for 'invalid_operator' for variable 'some_numeric_variable' of type 'numeric'.
cause The rule JSON specifies an operator (e.g., `invalid_operator`) that is not recognized or is not valid for the data type of the variable it's trying to operate on (e.g., using a text-only operator like `contains` on a numeric field).fixRefer to the `business-rules` documentation or source code for valid operators associated with each `FIELD_TYPE` (e.g., `FIELD_NUMERIC` supports `equal_to`, `greater_than`, `less_than`; `FIELD_TEXT` supports `contains`, `starts_with`, `ends_with`, etc.). Correct the operator name in your rule definition. -
NameError: name 'rule_set' is not defined
cause This error, or similar `AttributeError` messages related to `BaseRules.run()`, indicates an attempt to use an old API (`rule_set` or `BaseRules.run()`) from versions prior to 1.0. These methods have been replaced by the `run_all` function.fixUpdate your code to use `from business_rules.run_all import run_all` and structure your rule execution by passing `rules`, `defined_variables`, and `defined_actions` to `run_all` as shown in the quickstart example.
Warnings
- breaking Major API changes occurred in version 1.0 (August 2017), primarily refactoring `BaseRules` into `BaseVariables` and `BaseActions`, and introducing `run_all` as the main execution entry point. Code written for pre-1.0 versions will break.
- gotcha The `requires_python` metadata on PyPI for `business-rules` (e.g., `>=2.7, !=3.0.*, ... !=3.4.*`) is outdated and misleading. The library is fully compatible with Python 3.5+ up to at least 3.9 (and likely newer), as confirmed by release notes and PyPI classifiers. Don't be deterred by the old `requires_python` string.
- gotcha Rules are defined using a JSON-like dictionary structure, not directly in Python classes/methods. This requires rules to be loaded from a JSON file, a database, or constructed as a Python dictionary. Developers expecting a purely Pythonic DSL for rule *definition* might find this unexpected.
- gotcha Understanding the difference between 'all' and 'any' conditions in the rule JSON, and the `stop_on_first_failure` parameter in `run_all`, is crucial for correct rule evaluation logic. 'all' requires all sub-conditions to be true, 'any' requires at least one. `stop_on_first_failure` (default `False`) dictates if `run_all` stops after the first rule's conditions are not met.
Install
-
pip install business-rules
Imports
- BaseVariables
from business_rules.rules import BaseRules
from business_rules.variables import BaseVariables
- rule_variable
from business_rules.variables import rule_variable
- BaseActions
from business_rules.actions import BaseActions
- rule_action
from business_rules.actions import rule_action
- FIELD_NUMERIC
from business_rules.fields import FIELD_NUMERIC
- run_all
from business_rules.core import rule_set
from business_rules.run_all import run_all
Quickstart
from business_rules.actions import BaseActions, rule_action
from business_rules.fields import FIELD_NUMERIC, FIELD_TEXT
from business_rules.variables import BaseVariables, rule_variable
from business_rules.run_all import run_all
# 1. Define your data source (variables)
class SomeVariables(BaseVariables):
def __init__(self, some_object):
self.some_object = some_object
@rule_variable(FIELD_NUMERIC)
def some_numeric_variable(self):
return self.some_object.some_numeric_value
@rule_variable(FIELD_TEXT)
def some_text_variable(self):
return self.some_object.some_text_value
# 2. Define actions to take when rules are met
class SomeActions(BaseActions):
def __init__(self, some_object):
self.some_object = some_object
@rule_action(params=[{'fieldType': FIELD_NUMERIC, 'name': 'some_param'}])
def some_action(self, some_param):
self.some_object.some_action_has_run = True
self.some_object.some_action_param = some_param
# Example object to apply rules to
class MyDataObject:
some_numeric_value = 15
some_text_value = "hello world"
some_action_has_run = False
some_action_param = None
my_data_object = MyDataObject()
# 3. Define your rules in a JSON-like structure
rules = [{
"conditions": {
"all": [
{
"name": "some_numeric_variable",
"operator": "greater_than",
"value": 10
},
{
"name": "some_text_variable",
"operator": "contains",
"value": "hello"
}
]
},
"actions": [
{
"name": "some_action",
"params": {"some_param": 123}
}
]
}]
# 4. Run the rules
run_all(
rules=rules,
defined_variables=SomeVariables(my_data_object),
defined_actions=SomeActions(my_data_object),
stop_on_first_failure=False
)
print(f"Action has run: {my_data_object.some_action_has_run}")
print(f"Action parameter: {my_data_object.some_action_param}")