Issue 21794: stack frame contains name of wrapper method, not that of wrapped method (original) (raw)
def decorator(f): ... @functools.wraps(f) ... def new_func(self, junk): ... stack = inspect.stack() ... for i, frame in enumerate(stack): ... print("%s" % frame[3]) ... f(self, junk) ... return new_func ... @decorator ... def junk(self, p): ... print("%s" % junk.name) ... junk("a", "b") new_func junk junk.name 'junk'
Note that the wrapper function itself inspects the stack, printing out the names of methods on the stack. Note that "junk", the name of the wrapped function does not appear on the stack, it is only printed out by the junk method itself.
I think that the name of the function at the top of the stack should be the name of the wrapped function, not of its wrapper. The name of the wrapper function should not appear at all.
I don't think you understand how wrapping works. At the time you're enumerating the stack, the wrapped function has not actually been called. Only the wrapper has been called. If the stack included "junk" when junk had not yet been executed, the stack would be a lie.
From the interpreter's point of view, it doesn't even know that wrapping is in play, aside from the chain of wrapped values attached to the wrapper by functools.wraps (as a convenience). Until you actually call the wrapped function, it's possible you could change your mind and call some other function instead; Python won't stop you, and Python can't tell the difference before the call has been made.
I agree with Josh; I don't think there's any bug here. To confirm, I expanded on your example, reproduced below. Have a look at and play around with this, and if you still believe something is wrong, ask on python-list and the folks there should be able to help. If you determine that there really is a bug, please reopen the issue.
""" import functools import inspect
def lineno(): return inspect.getlineno(inspect.stack()[1][0])
try: # just to make it easy to see code and output together... with open(file) as file: data = file.read() print(data[:data.rfind("# output:") + 9]) except Exception: pass
print('#line {}: defining decorator'.format(lineno()))
def decorator(f): print('# line {}: decorator called'.format(lineno())) print('# line {}: defining inner'.format(lineno())) @functools.wraps(f) def inner(arg1, arg2): print('# line {}: inner called, arg1 {} arg2 {}'.format(lineno(), arg1, arg2)) for i, frame in enumerate(inspect.stack()): print("# line {}: printed in inner, frame {}, name {}".format(lineno(), i, frame[3])) f(arg1 + 1 , arg2 + 1)
print('# line {}: printed in decorator, inner.__name__ == {}'.format(lineno(),inner.__name__))
print('# line {}: printed in decorator, f.__name__ == {}'.format(lineno(), f.__name__))
return inner
print('#line {}: defining wrapped'.format(lineno()))
@decorator def wrapped(warg1, warg2): print('# line {}: wrapped called, warg1 {} warg2 {}'.format(lineno(), warg1, warg2)) print("# line {}: printed in wrapped, wrapped.name == {}".format(lineno(), wrapped.name)) stack = inspect.stack() for i, frame in enumerate(stack): print("# line {}: printed in wrapped, frame {}, name {}".format(lineno(), i, frame[3]))
print('#line {}: Calling wrapped...'.format(lineno())) wrapped(1,2)
print("#line {}: done".format(lineno()))
output:
expected output:
#line 13: defining decorator #line 29: defining wrapped
line 16: decorator called
line 17: defining inner
line 25: printed in decorator, inner.name == wrapped
line 26: printed in decorator, f.name == wrapped
#line 39: Calling wrapped...
line 20: inner called, arg1 1 arg2 2
line 22: printed in inner, frame 0, name inner
line 22: printed in inner, frame 1, name
line 33: wrapped called, warg1 2 warg2 3
line 34: printed in wrapped, wrapped.name == wrapped
line 37: printed in wrapped, frame 0, name wrapped
line 37: printed in wrapped, frame 1, name inner
line 37: printed in wrapped, frame 2, name
#line 42: done """