https://github.com/miguelteixeiraa/crinkle
Crinkle is a framework for organizing the execution of complex processing flows by implementing the “Chain of Responsability” pattern in Python
https://github.com/miguelteixeiraa/crinkle
apache-commons async asyncio chain-of-responsibility-pattern framework function-composition opinionated pydantic python python-types python3 simple type-safe typehints types
Last synced: 2 months ago
JSON representation
Crinkle is a framework for organizing the execution of complex processing flows by implementing the “Chain of Responsability” pattern in Python
- Host: GitHub
- URL: https://github.com/miguelteixeiraa/crinkle
- Owner: miguelteixeiraa
- License: mit
- Created: 2023-12-04T02:41:59.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2024-03-11T22:13:10.000Z (over 1 year ago)
- Last Synced: 2025-03-28T14:51:07.643Z (3 months ago)
- Topics: apache-commons, async, asyncio, chain-of-responsibility-pattern, framework, function-composition, opinionated, pydantic, python, python-types, python3, simple, type-safe, typehints, types
- Language: Python
- Homepage:
- Size: 105 KB
- Stars: 11
- Watchers: 2
- Forks: 0
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
![]()
Crinkle
Crinkle is 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 less_ code.
- **Grow large**: Grow large while keep everything maintainable and fast
- **Empower testability**: Make things more testable by breaking large processes into small, specialized pieces with composition.
- **Opinionated Yet Intuitive**: Quickly understand and work with.---
> 💡 Inspired by [Apache Commons Chain](https://www.baeldung.com/apache-commons-chain)
## Requirements
A recent and currently supported version of [Python](https://www.python.org/downloads/").
## Installation
```console
$ pip install crinkle
```## Example
For an introduction to the Chain of Responsability Pattern and its use cases, see
[Chain of Responsibility by Refactoring Guru](https://refactoring.guru/design-patterns/chain-of-responsibility)```Python hl_lines="18 21 23-27"
from pydantic import BaseModel
from typing import Dict, List
from crinkle import Context, Flowclass Order(BaseModel):
loyalty: bool # just to simplify the example
items: List
discounts: Listcontext = Context[Order, Dict](
initial_state=Order(),
additional_data={}, # Optional
)flow = Flow(name='Promotions/Discounts Flow')
@flow.processor(name='Discounts pre-conditions Processor')
def discounts_pre_conditions_processor(context: Context[Order, Dict]) -> bool:
if context.state.loyalty:
return False # Go to next processorreturn True # Stop flow without going to next processors
@flow.processor(name='Manufacturer Coupons Processor')
def manufacturer_coupons_processor(context: Context[Order, Dict]) -> bool:
# do stuff with context
return False # Go to next processor# Async is also supported
@flow.processor(name='Buy one get one Processor')
async def bogo_processor(context: Context[Order, Dict]) -> bool:
# do stuff with context
return False # 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 processing
```### Using Object Oriented Programming (OOP)
Crinkle has flavors for all tastes! OOP is also supported, see example:
```Python hl_lines="15-18"
from typing import Dict, List
from crinkle import ProcessorBase, ProcessorBaseAsync, Flow, Contextclass Order(BaseModel):
loyalty: bool # just to simplify the example
items: List
discounts: Listcontext = Context[Order, Dict](
initial_state=Order(),
additional_data={}, # Optional
)class DiscountsPreConditionsProcessor(ProcessorBase):
def __init__(self, name: str):
self.name = namedef process(self, context: Context[Order, Dict]) -> bool:
# Go to next processor
if context.state.loyalty:
return Falsereturn True # Stop flow without going to next processors
class ManufacturerCouponsProcessor(ProcessorBase):
def __init__(self, name: str):
self.name = namedef process(self, context: Context[Order, Dict]) -> bool:
# do stuff with context
return False # Go to next processorclass BOGOProcessor(ProcessorBaseAsync):
def __init__(self, name: str):
self.name = name# Async is also supported
async def process(self, context: Context[Order, Dict]) -> bool:
# do stuff with context
return False # This is the last processor, so end of flowflow = Flow(
flow_name='Promotions/Discounts Flow',
processors=[
DiscountsPreConditionsProcessor(name='...'),
ManufacturerCouponsProcessor(name='...'),
BOGOProcessor(name='...'),
],
)
# OR
flow.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 processing
```
## License
This project is licensed under the terms of the [MIT license](https://github.com/miguelteixeiraa/crinkle/blob/main/LICENSE).