3

I want to get the co_firstlineno for function in static way, The unwrapped function is ok, But if a method is wrapped, I can only get the lineno where the wrapper function is located.

md.py

import functools

def run(func):
    @functools.wraps(func)
    def warper(*args, **kwargs):
        res = func()
        return res
    return warper

def func_unwrapper():
    pass

@run
def func_with_wrapper():
    pass

run.py

from importlib import util as module_util
import inspect

def load_moudle_by_path(path):
    foo = module_util.spec_from_file_location('md', path)
    md = module_util.module_from_spec(foo)
    foo.loader.exec_module(md)
    return md

def get_line():
    md = load_moudle_by_path('md.py')
    for name, o in inspect.getmembers(md):
        if inspect.isfunction(o):
            print('[{}]{}'.format(name, o.__code__.co_firstlineno))

get_line()


>>>
[func_unwrapper]10
[func_with_wrapper]4
[run]3

enter image description here

Alex
  • 150
  • 9

1 Answers1

3

I see what you were hoping for, but the behaviour is expected behaviour.

func_with_wrapper really is running the code at line 4, with the code at line 13 (which is what you were probably hoping for / expecting) only being called based on the func parameter passed (the func_with_wrapper argument).

After your own research, finding .__wrapped__, which functools graciously adds, there's no need to add something similar yourself.

Your original code will work, if you update:

def get_line():
    md = load_module_by_path('md.py')
    for name, o in inspect.getmembers(md):
        if inspect.isfunction(o):
            try:
                if o.__wrapped__:
                    print('decorated [{}]{}'.format(name, o.__wrapped__.__code__.co_firstlineno))
            except AttributeError:
                print('undecorated [{}]{}'.format(name, o.__code__.co_firstlineno))

Output:

undecorated [func_unwrapper]10
decorated [func_with_wrapper]13
undecorated [run]3
Grismar
  • 27,561
  • 4
  • 31
  • 54
  • Oh! It's work fine. I found the update_wrapper() in functools.py that it saved the original func with "wrapper.__wrapped__ = wrapped",But I can't access in inspect.getmembers(md), prompting:'function' object has no attribute'__wrapped__' – Alex Oct 14 '20 at 03:29
  • Thank you so much for your reply and advice. – Alex Oct 14 '20 at 05:02