0

I was trying to learn about decorators, and was trying to write a decorator for a class that would wrap all the methods in the class (except the internal ones). I can see that it's trying to wrap the methods I want wrapped, but then when I try to call any of those methods, it seems that __init__() was wrapped.

#!/usr/bin/python

import sys
import types
from functools import wraps

def classTracer(cls):
    for name,f in vars(cls).items():
        if isinstance(f, types.FunctionType) and not name.startswith('__'):
            print "I am wrapping method", name
            @wraps(f)
            def wrapper(*args, **kwargs):
                print "Entering %s()" % f.__name__
                rval = f(*args, **kwargs)
                print "Exited %s(), returning" % f.__name__, rval
                return rval
            setattr(cls, name, wrapper)
    return cls

@classTracer
class MyClass(object):
    def __init__(self):
        self.x = 1
    def foo(self, i):
        return i+7
    def bar(self, i):
        return i-7

def main():
    print 'create object'
    x = MyClass()
    print 'call bar'
    print x.bar(7)
    return 0

if __name__ == "__main__":
    sys.exit(main())

The output:

I am wrapping method bar
I am wrapping method foo
create object
call bar
Entering __init__()
Traceback (most recent call last):
  File "./test2.py", line 35, in <module>
    sys.exit(main())
  File "./test2.py", line 31, in main
    print x.bar(7)
  File "./test2.py", line 14, in wrapper
    rval = f(*args, **kwargs)
TypeError: __init__() takes exactly 1 argument (2 given)

Where am I going wrong?


Edit: thanks to the linked question, I was able to modify my wrapper:

def classTracer(cls):
    def gen_wrapper(name, f):
        @wraps(f)
        def wrapper(*args, **kwargs):
            print "Entering %s()" % name
            rval = f(*args, **kwargs)
            print "Exited %s(), returning" % name, rval
            return rval
        return wrapper
    for name,f in vars(cls).items():
        if isinstance(f, types.FunctionType) and not name.startswith('__'):
            print "I am wrapping method", name
            setattr(cls, name, gen_wrapper(name, f))
    print 'loop done, last function was', name
    return cls
Edward Falk
  • 9,991
  • 11
  • 77
  • 112
  • 2
    Your `wrapper`s look up `f` too late. – user2357112 Oct 17 '19 at 01:07
  • Ahh, so `f` is dereferenced when `wrapper()` executes, not when it's defined, by which time `f` is `__init__()` via the dumb luck that `__init__()` was the last thing in `vars(cls)`. Somehow, I need to bind `f` when `wrapper()` is defined. – Edward Falk Oct 17 '19 at 01:18

0 Answers0