import functools import operator import inspect from typing import Callable, Any, Dict, List class Rule: def __init__(self, func: Callable[[Dict[str, Any]], bool], name: str = None): self.func = func self.name = name or func.__name__ def __call__(self, context: Dict[str, Any]) -> bool: return self.func(context) def __and__(self, other: "Rule") -> "Rule": return Rule(lambda ctx: self(ctx) and other(ctx), name=f"({self.name} AND {other.name})") def __or__(self, other: "Rule") -> "Rule": return Rule(lambda ctx: self(ctx) or other(ctx), name=f"({self.name} OR {other.name})") def __invert__(self) -> "Rule": return Rule(lambda ctx: not self(ctx), name=f"(NOT {self.name})") class RuleEngine: def __init__(self): self.rules: List[Rule] = [] def register(self, rule: Rule): self.rules.append(rule) def evaluate(self, context: Dict[str, Any]) -> Dict[str, bool]: results = {} for rule in self.rules: try: results[rule.name] = rule(context) except Exception as e: results[rule.name] = False return results def auto_rule(fn): sig = inspect.signature(fn) def wrapper(ctx): bound = {k: ctx.get(k) for k in sig.parameters} return fn(**bound) return Rule(wrapper, name=fn.__name__) @auto_rule def is_adult(age): return age >= 18 @auto_rule def high_income(income): return income > 5000 @auto_rule def country_allowed(country): return country in {"CL", "MX", "CO"} engine = RuleEngine() engine.register(is_adult & high_income | country_allowed) engine.register(~high_income & is_adult) context = {"age": 25, "income": 3000, "country": "CL"} print(engine.evaluate(context))