from typing import Callable import inspect from typing import Protocol from abc import abstractmethod def _create_method(to_method_name: str, to_attr_name: str): # returns a closure that traps the name of the method to invoke and the attribute_name of the object that will act as receiver-self def new_method(self, *args, **kargs): inner_self = getattr(self, to_attr_name) to_method = getattr(inner_self, to_method_name) # bound method (to inner_self) return to_method(*args, **kargs) return new_method # decorator with parameters, so it has to return a function (that will be invoked with the class being decorated) # we don't create a new class, we add functions to the existing class and return it def delegate_methods(method_names: list[str], to_attr_name: str): # decorator with parameters # receives a list of method names to create and delegate from them to the corresponding method in the object indicated by to_attr_name def add_methods(cls): for method_name in method_names: setattr(cls, method_name, _create_method(method_name, to_attr_name)) return cls return add_methods def delegate_interface(interface_like, to_attr_name: str): # decorator with parameters # receives an "interface" for which methods we will create "delegator methods" to delegate from them to the corresponding method in the object indicated by to_attr_name def add_methods(cls): method_names = [name for name, func in inspect.getmembers(interface_like, predicate=inspect.isfunction) if name != "__init__"] for method_name in method_names: setattr(cls, method_name, _create_method(method_name, to_attr_name)) return cls return add_methods ######################################### print("- Testing delegate_methods") class Formatter: def short_format(self, txt: str, prepend: str): return f"{prepend}{txt}" def long_format(self, txt: str, wrap: str): return f"{wrap}{txt}{wrap}" @delegate_methods(["short_format", "long_format"], "formatter") class TextHelper: def __init__(self, id_, formatter): self.id = id_ self.formatter = formatter def beautify(self, txt) -> str: return f"beautifying {self}" helper = TextHelper("aa", Formatter()) print(helper.long_format("hi", "||")) print(helper.short_format("hi", "||")) print("##################################") print("- Testing delegate_interface") class Formattable(Protocol): @abstractmethod def short_format(self, txt: str, prepend: str): pass @abstractmethod def long_format(self, txt: str, wrap: str): pass class FormattableImp(Formattable): def short_format(self, txt: str, prepend: str): return f"{prepend}{txt}" def long_format(self, txt: str, wrap: str): return f"{wrap}{txt}{wrap}" @delegate_interface(Formattable, "formatter") class TextHelper2: def __init__(self, id_, formatter): self.id = id_ self.formatter = formatter def beautify(self, txt) -> str: return f"beautifying {self}" helper = TextHelper2("aa", FormattableImp()) print(helper.long_format("hi", "||")) print(helper.short_format("hi", "||"))