Search code examples
pythonclassdecoratorpython-2.x

decorator in python 2 that decorate given method of given class


I have given arbitraly class A with method m and I would like to create decorator that can be added to new function f and this decorator will allow to execute f every time that m is called but to execute f before m and f should intercept arguments of m

I need help defining pre_execution decorator - I have something but it's not working and I can't figure out how to make it work

#a.py
class A:
    def m(self, x):
        return x+1

#mydecorator.py     -- need help with code in this file
def pre_execution(klass, method):
    old_fn = getattr(klass, method)

    def inner(fn, *args):
        # @wraps(fn)
        def iin(*args, **kwargs):
            fn(*args, **kwargs)
            return old_fn(*args, **kwargs)
        return iin
    setattr(klass, method, inner)
    return inner


# main.py
from a import A
from mydecorator import pre_execution

if __name__ == "__main__":
    @pre_execution(A, 'm')
    def f(*args, **kwargs):
        print "in"
        print "my code using args and kwargs"
        print "out"

    a = A()
    print a.m(1) == 2
    print a.m(1)

expected output:

in
my code using args and kwargs
out
True

Solution

  • I think what you want is

    def pre_execution(klass, method):
        old_method = getattr(klass, method)
    
        def patch_klass(f):
            def new_method(*args, **kwargs):
                f(*args, **kwargs)
                return old_method(*args, **kwargs)
            setattr(klass, method, new_method)
            return f
        return patch_klass
    

    pre_execution saves a reference to the original method, then defines a function that will be returned and called on f. This function defines a new method that calls f before calling the original method. patch_klass then replaces the old method with the new in. It also returns the original function unmodified, in case you want to use f elsewhere.

    $ python tmp.py
    in
    my code using args and kwargs
    out
    True
    in
    my code using args and kwargs
    out
    2
    

    Note that this works in Python 2 or 3.