Package: rules-engine
Getting StartedDescriptionSimple rules engine inspired byMartin Fowler's blog post in
2009andfunnel-rules-engine.Full Documentation can be foundhereRequirementspython >= 3.6How to installpip install rules-engineHow to usefromrules_engineimportRule,RulesEngine,when,thenname="fyndiq"RulesEngine(Rule(when(name=="fyndiq"),then(True))).run(name)>>>TrueWhenEvaluates a condition.let's check if a value isNoneand raise an exception.fromrules_engineimportRule,RulesEngine,whenobj=Nonedefcannot_be_none_error():return"not a string error"RulesEngine(Rule(when(objisNone),cannot_be_none_error)).run(obj)>>>'not a string error'ThenEvaluates an action.fromrules_engineimportRule,RulesEngine,whenobj=NoneRulesEngine(Rule(when(objisNone),then('not a string error'))).run(obj)>>>'not a string error'NotThenot_keyword is a logical operator.The return value will beTrueif the statement(s) are notTrue, otherwise it will returnFalse.fromrules_engineimportRule,RulesEngine,not_defis_missing(obj):returnnotobjobj="Hello"RulesEngine(Rule(not_(is_missing),then(True))).run(obj)>>>TrueAllEvaluates multiple conditions and if all conditions areTruethe action is executedExample:We need to check if an objectobjis not missing and is of typelistfromrules_engineimportRule,RulesEngine,all_defis_missing(obj):returnnotobjdefis_a_list(obj):returnisinstance(obj,list)obj=[1,2]RulesEngine(Rule(all_(not_(is_missing),is_a_list),then(True))).run(obj)>>>TrueAnyEvaluates multiple conditions and if any of the conditions isTruethe action is executedExample:We need to check if an objectobjis astror alistfromrules_engineimportRule,RulesEngine,any_defis_a_str(obj):returnisinstance(obj,str)defis_a_list(obj):returnisinstance(obj,list)obj="Hello"RulesEngine(Rule(any_(is_a_str,is_a_list),then(True))).run(obj)>>>TrueRun/RunAllRunRuns rules sequentially and exists executes the action for the first passing condition.fromrules_engineimportRule,RulesEngine,thenobj=Nonedefis_integer(value):returnisinstance(value,int)defis_string(value):returnisinstance(value,str)value=1234RulesEngine(Rule(is_integer,then("integer")),Rule(is_string,then("string")),).run(value)>>>"integer"Since the first rule satisfies the conditions the rules engine will go no furtherRunAllEvaluates all conditions and adds them to a listfromrules_engineimportRule,RulesEngine,thendefis_integer(value):returnisinstance(value,int)defis_string(value):returnisinstance(value,str)defis_gr_3_chars(value):returnlen(value)>3value="Hello"RulesEngine(Rule(is_integer,then("integer")),Rule(is_string,then("string")),Rule(is_gr_3_chars,then("greater than 3 charcters")),).run_all(value)>>>["string","greater than 3 charcters"]Full ExampleIn order for an article to be completed it must have the following rulesstock is > 0.image url is present.price exists.fromcollectionsimportnamedtuplefromtypingimportUnionfromrules_engineimportOtherwise,Rule,RulesEngine,thenArticle=namedtuple("Article","title price image_url stock")article=Article(title="Iphone Case",price=1000,image_url="http://localhost/image",stock=None)defproduce_article_completed_event():returnNonedefarticle_controller(article:Article):ifnotarticle.stock:returnFalseifnotarticle.price:raiseValueError("Article price missing")ifnotarticle.image_url:raiseValueError("Article image_url missing")returnproduce_article_completed_event()To be able to change to rules engine, we need to split the conditions and actions.Rules engine is pretty simple if the condition isTrue, its corresponding action will be executed.### Conditionsdefno_article_stock(article):returnnotarticle.stockdefno_article_price(article):returnnotarticle.pricedefno_article_image_url(article):returnnotarticle.image_url### Actionsdefarticle_price_missing_error(article):raiseValueError("Article price missing")defarticle_image_missing_error(article):raiseValueError("Article image_url missing")### Rules Enginedefarticle_complete_rules(article):RulesEngine(Rule(no_article_stock,then(False)),Rule(no_article_price,article_price_missing_error),Rule(no_article_image_url,article_image_missing_error),Otherwise(produce_article_completed_event()),).run(article)