从堆栈(框架)对象中获取函数对象

时间:2019-02-12 18:47:31

标签: python-3.x

我为模块logging编写了一个自定义日志记录类,我将其称为call。希望通过此类,将其放在任何函数/方法中,并记录函数名称及其参数和调用函数的所有值。

这对于类方法很好

Foo.bar(self, a=1, b=2, c=3, *args=(), **kwargs={'something': 4})

使用这个最小的示例

import logging
import inspect

def call(logger):
    fname = []  # Function name including module and class
    fargs = []  # Arguments of function including positional and named arguments

    parentframe = inspect.stack()[1][0]
    module      = inspect.getmodule(parentframe)

    if module and module.__name__ != "__main__":
        fname.append(module.__name__)

    codename = parentframe.f_code.co_name

    if "self" in parentframe.f_locals:
        fname.append(parentframe.f_locals["self"].__class__.__name__)

    fobj = getattr(parentframe.f_locals["self"].__class__, codename)

    if codename != "<module>":
        fname.append(codename)

    argspec = inspect.formatargspec(*inspect.getfullargspec(fobj))
    args = argspec[1:-1].split(",")

    for arg in args:
        argkey = arg.strip().replace("*", "").split("=")[0]
        if arg == "self":
            fargs.append("self")
        else:
            fargs.append(arg.split("=")[0] + "=" + str(parentframe.f_locals[argkey]))

    del parentframe

    msg = ".".join(fname) + "(" + ",".join(fargs) + ")"
    if logger.isEnabledFor(30):
        logger.log(30, msg)


class Foo:

    def __init__(self, l):
        self.logger = l

    def bar(self, a, b, c=3, *args, **kwargs):
        call(self.logger)


if __name__ == "__main__":
    logging.addLevelName(30, "CALL")
    logger = logging.getLogger('blub')
    logger.level = 20
    f = Foo(logger)
    f.bar(1, 2, something=4)
    print("done...")

我的问题是,当我在静态方法或简单函数上使用相同的功能时。在使用fobj = getattr(parentframe.f_locals["self"].__class__, codename) 获取函数对象(self)的行中,它失败了。

在我假设的问题中,

parentframe是函数的Frame对象。我尚未找到从该对象获取功能对象的方法。有办法吗?

1 个答案:

答案 0 :(得分:0)

使用getattr(module, codename)获取类中不包含的函数的函数对象。

完整代码如下:

import logging
import inspect

def call(logger):
    fname = []  # Function name including module and class
    fargs = []  # Arguments of function including positional and named arguments

    parentframe = inspect.stack()[1][0]
    module      = inspect.getmodule(parentframe)

    if module and module.__name__ != "__main__":
        fname.append(module.__name__)

    codename = parentframe.f_code.co_name

    if "self" in parentframe.f_locals:
        fname.append(parentframe.f_locals["self"].__class__.__name__)
        fobj = getattr(parentframe.f_locals["self"].__class__, codename)
    else:
        fobj = getattr(module, codename)

    if codename != "<module>":
        fname.append(codename)

    argspec = inspect.formatargspec(*inspect.getfullargspec(fobj))
    args = argspec[1:-1].split(",")

    for arg in args:
        argkey = arg.strip().replace("*", "").split("=")[0]
        if arg == "self":
            fargs.append("self")
        else:
            fargs.append(arg.split("=")[0] + "=" + str(parentframe.f_locals[argkey]))

    del parentframe

    msg = ".".join(fname) + "(" + ",".join(fargs) + ")"
    if logger.isEnabledFor(30):
        logger.log(30, msg)


class Foo:

    def __init__(self, l):
        self.logger = l

    def bar(self, a, b, c=3, *args, **kwargs):
        call(self.logger)


def boo(a, b, c=3, *args, **kwargs):
    call(logger)


if __name__ == "__main__":
    logging.addLevelName(30, "CALL")
    logger = logging.getLogger('blub')
    logger.level = 20
    f = Foo(logger)
    f.bar(1, 2, something=4)
    boo(1, 2, something=4)
    print("done...")