Dynamic decorator for regex matching class methods

208 Views Asked by At

I want to simplify method decoration in situations like delegate class implementations.

  • Suppose a 3rd party "service" class with tenths of methods
  • You want to override a good number of methods just to be able to decorate them
  • You create a delegate subclass and override selected methods

That situation leads to something like:

class ServiceDelegate(Service):
    @decorator
    def service_method1(self):
        super().service_method1()

    @decorator
    def service_method2(self):
        super().service_method2()

    @decorator
    def service_method3(self):
        super().service_method3()

    ...

This solution doesn't seems very productive. For this reason something like this could be implemented:

def methods_decorator(deco, regex: str, base_class=None):
    def f(c: type):
        nonlocal base_class
        # Default base class is the decorated class itself
        if not base_class:
            base_class = c
        avoid_functions: List[str] = ["__init__","__doc__","__module__"]
        # regex functions
        def lmd(i): return i[0] not in avoid_functions and re.fullmatch(regex, i[0])
        funcs = list(filter(lmd, base_class.__dict__.items()))

        for func in funcs:
            print(str(type(func[1])))
            if type(func[1]) == FunctionType:
                setattr(c, func[0], deco(func[1]))
            elif type(func[1]) == StaticMethodType:
                setattr(c, func[0], staticmethod(deco(func[1])))
            elif type(func[1]) == ClassMethodType:
                setattr(c, func[0], classmethod(deco(func[1])))
        return c
    return f


@methods_decorator(deco=decorator1, regex="service_method.*", base_class=Service)
@methods_decorator(deco=decorator2, regex="other_method.*", base_class=Service)
@methods_decorator(deco=decorator3, regex="new.*")
class ServiceDelegate(Service): 
    def new_method(self):
        # Some new func here
    ...

The idea is to "copy" and decorate regex selected functions from the base class without having to override them. This implementation works for me in my specific scenario.

I'm pretty new to python so I'm not really sure if this is a good approach or could be a bad practice.

  • Are there any reasons to not apply this kind of solution?
  • Are there existing python packages providing similar functionality?
1

There are 1 best solutions below

1
On

The solution is for typing less. However, it just moves complexities from adding method decorators to naming methods and adding a magic class decorator.

Changing the name of the method decorator and names of methods is a better idea.

class ServiceDelegate(Service):
    @service
    def method1(self):
        super().method1()

    @service
    def method2(self):
        super().method2()

    @service
    def method3(self):
        super().method3()

Omitting the service_ prefix and adding @service make exactly no change of the amount of typed characters and the magic class decorator is not required anymore.