This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: stack frame contains name of wrapper method, not that of wrapped method
Type: behavior Stage: resolved
Components: Interpreter Core Versions: Python 2.7
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: josh.r, the.mulhern, zach.ware
Priority: normal Keywords:

Created on 2014-06-17 19:12 by the.mulhern, last changed 2022-04-11 14:58 by admin. This issue is now closed.

Messages (3)
msg220863 - (view) Author: the mulhern (the.mulhern) Date: 2014-06-17 19:12
>>> 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
<module>
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.
msg220865 - (view) Author: Josh Rosenberg (josh.r) * (Python triager) Date: 2014-06-17 19:23
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.
msg220954 - (view) Author: Zachary Ware (zach.ware) * (Python committer) Date: 2014-06-18 20:56
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 <module>
#  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 <module>
#line 42: done
"""
History
Date User Action Args
2022-04-11 14:58:05adminsetgithub: 65993
2014-06-18 20:56:57zach.waresetstatus: open -> closed

nosy: + zach.ware
messages: + msg220954

resolution: not a bug
stage: resolved
2014-06-17 19:23:24josh.rsetnosy: + josh.r
messages: + msg220865
2014-06-17 19:12:26the.mulherncreate