Loading...
Loading...
Implements the Chain of Responsibility pattern in Python. Use when the user mentions chain of responsibility, CoR, or when you need to chain handlers that each process and pass to the next—validation pipelines, processing steps, transformation chains, or any sequential pipeline.
npx skill4agent add progmichaelkibenko/top-coder-agent-skills chain-of-responsibility-pythonhandle(context)| Role | Responsibility |
|---|---|
| Handler (protocol/ABC) | Declares |
| Base handler (optional) | Holds |
| Concrete handlers | Implement |
| Client | Builds the chain (e.g. |
valuefield_nameerrors# One function; every new rule forces edits.
def validate_order_input(data: OrderInput) -> list[dict]:
errors = []
if not (data.email or "").strip():
errors.append({"field": "email", "message": "email is required"})
elif not re.match(r"^[^@]+@[^@]+\.\w+$", data.email):
errors.append({"field": "email", "message": "invalid email"})
if data.amount is None or not (1 <= data.amount <= 10000):
errors.append({"field": "amount", "message": "amount must be 1-10000"})
return errors# validators/base.py
from typing import Protocol, runtime_checkable, Optional
from dataclasses import field, dataclass
@runtime_checkable
class Validator(Protocol):
def validate(self, context: "ValidationContext") -> None: ...
def set_next(self, next_validator: "Validator") -> "Validator": ...
@dataclass
class ValidationContext:
value: any
field_name: str
errors: list = field(default_factory=list)
class BaseValidator:
def __init__(self) -> None:
self._next: Optional[Validator] = None
def set_next(self, next_validator: Validator) -> Validator:
self._next = next_validator
return next_validator
def validate(self, context: ValidationContext) -> None:
if self._next:
self._next.validate(context)# validators/required.py
from .base import BaseValidator, ValidationContext
class RequiredValidator(BaseValidator):
def validate(self, context: ValidationContext) -> None:
if context.value is None or str(context.value).strip() == "":
context.errors.append({
"field": context.field_name,
"message": f"{context.field_name} is required",
})
super().validate(context)
# validators/email_format.py
import re
EMAIL_RE = re.compile(r"^[^@]+@[^@]+\.\w+$")
class EmailFormatValidator(BaseValidator):
def validate(self, context: ValidationContext) -> None:
if context.value and not EMAIL_RE.match(str(context.value)):
context.errors.append({"field": context.field_name, "message": "invalid email format"})
super().validate(context)
# validators/range.py
class RangeValidator(BaseValidator):
def __init__(self, min_val: float, max_val: float) -> None:
super().__init__()
self.min_val = min_val
self.max_val = max_val
def validate(self, context: ValidationContext) -> None:
try:
n = float(context.value) if context.value is not None else None
except (TypeError, ValueError):
n = None
if context.value is not None and (n is None or n < self.min_val or n > self.max_val):
context.errors.append({
"field": context.field_name,
"message": f"must be between {self.min_val} and {self.max_val}",
})
super().validate(context)# services/order_validation.py
from validators.required import RequiredValidator
from validators.email_format import EmailFormatValidator
from validators.range import RangeValidator
from validators.base import ValidationContext
email_chain = RequiredValidator()
email_chain.set_next(EmailFormatValidator())
amount_chain = RequiredValidator()
amount_chain.set_next(RangeValidator(1, 10_000))
def validate_order_input(data: OrderInput) -> list[dict]:
ctx = ValidationContext(value=None, field_name="", errors=[])
ctx.value, ctx.field_name = data.email, "email"
email_chain.validate(ctx)
ctx.value, ctx.field_name = data.amount, "amount"
amount_chain.validate(ctx)
return ctx.errorsvaluefield_nameerrors: listsuper().validate(context)typing.ProtocolBaseValidatorset_nextrequired.set_next(email_format).set_next(amount_range)validators/email_format.pyvalidators/base.py| Feature | Pipeline | Chain of Responsibility |
|---|---|---|
| Execution | Fixed, mandatory sequence | Conditional; handler decides whether to pass to the next |
| Flow | Linear, no branching | Allows flexible termination and branching |
| Termination | Runs to completion (barring errors) | Can be terminated early by a handler |
| Use cases | Data processing, parsing, ETL | Event handling, approval workflows, validation, message filtering |