Package: crinkle
CrinkleCrinkleis a framework for organizing the execution of complex processing flows by implementing the “Chain of Responsability” pattern (🔋included).Merchan:Minimalist: Translate complex computational requirements into simple, yet elegant.and lesscode.Grow large: Grow large while keep everything maintainable and fastEmpower testability: Make things more testable by breaking large processes into small, specialized pieces with composition.Opinionated Yet Intuitive: Quickly understand and work with.💡 Inspired byApache Commons ChainRequirementsA recent and currently supported version ofPython.Installation$pipinstallcrinkleExampleFor an introduction to the Chain of Responsability Pattern and its use cases, seeChain of Responsibility by Refactoring GurufrompydanticimportBaseModelfromtypingimportDict,ListfromcrinkleimportContext,FlowclassOrder(BaseModel):loyalty:bool# just to simplify the exampleitems:Listdiscounts:Listcontext=Context[Order,Dict](initial_state=Order(),additional_data={},# Optional)flow=Flow(name='Promotions/Discounts Flow')@flow.processor(name='Discounts pre-conditions Processor')defdiscounts_pre_conditions_processor(context:Context[Order,Dict])->bool:ifcontext.state.loyalty:returnFalse# Go to next processorreturnTrue# Stop flow without going to next processors@flow.processor(name='Manufacturer Coupons Processor')defmanufacturer_coupons_processor(context:Context[Order,Dict])->bool:# do stuff with contextreturnFalse# Go to next processor# Async is also supported@flow.processor(name='Buy one get one Processor')asyncdefbogo_processor(context:Context[Order,Dict])->bool:# do stuff with contextreturnFalse# This is the last processor, so end of flow# Just like Apache Commons Chain, the Flow will be forced to terminate# if a processor returns True (this would mean processing is complete).flow.execute(context)# **context.state** has the state of the Order after all processingUsing Object Oriented Programming (OOP)Crinkle has flavors for all tastes! OOP is also supported, see example:fromtypingimportDict,ListfromcrinkleimportProcessorBase,ProcessorBaseAsync,Flow,ContextclassOrder(BaseModel):loyalty:bool# just to simplify the exampleitems:Listdiscounts:Listcontext=Context[Order,Dict](initial_state=Order(),additional_data={},# Optional)classDiscountsPreConditionsProcessor(ProcessorBase):def__init__(self,name:str):self.name=namedefprocess(self,context:Context[Order,Dict])->bool:# Go to next processorifcontext.state.loyalty:returnFalsereturnTrue# Stop flow without going to next processorsclassManufacturerCouponsProcessor(ProcessorBase):def__init__(self,name:str):self.name=namedefprocess(self,context:Context[Order,Dict])->bool:# do stuff with contextreturnFalse# Go to next processorclassBOGOProcessor(ProcessorBaseAsync):def__init__(self,name:str):self.name=name# Async is also supportedasyncdefprocess(self,context:Context[Order,Dict])->bool:# do stuff with contextreturnFalse# This is the last processor, so end of flowflow=Flow(flow_name='Promotions/Discounts Flow',processors=[DiscountsPreConditionsProcessor(name='...'),ManufacturerCouponsProcessor(name='...'),BOGOProcessor(name='...'),],)# ORflow.add_processor(DiscountsPreConditionsProcessor(name='...'))flow.add_processor(ManufacturerCouponsProcessor(name='...'))flow.add_processor(BOGOProcessor(name='...'))# Just like Apache Commons Chain, the Flow will be forced to terminate# if a processor returns True (this would mean processing is complete).flow.execute(context)# **context.state** has the state of the Order after all processingLicenseThis project is licensed under the terms of theMIT license.