Skip to content Skip to sidebar Skip to footer

Get Function Object From Stack (frame) Object

I have written a custom logging class for module logging that I called call. With this class I hope to place it in any function/method and it logs the function name with its argume

Solution 1:

Use getattr(module, codename) to get the function-object of functions that are not contained in classes.

Here the full code:

import logging
import inspect

defcall(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)


classFoo:

    def__init__(self, l):
        self.logger = l

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


defboo(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...")

Solution 2:

Taking ideas from both, I wrote this function to find the function object from a frame. I'm sure there's some edge cases around inherited staticmethods, and obviously any code not using the cls and self conventions for param names. This also doesn't work for lambdas... but you shouldn't be logging anything out in a lamba anyway :-P

def_get_func_obj(f):
    """
    Get function object from a frame. If it can't find it, return None
    """

    codename = f.f_code.co_name
    fobj = Nonetry:
        if"self"in f.f_locals:  # regular method
            fobj = getattr(f.f_locals["self"].__class__, codename)
        elif"cls"in f.f_locals:  # class method
            fobj = getattr(f.f_locals["cls"], codename)
        else:
            module = inspect.getmodule(f)  # only fetch module if we need itifhasattr(module, codename):  # regular module level function
                fobj = getattr(module, codename)
            else:  # static method
                classes = [
                    getattr(module, name)
                    for name indir(module)
                    if inspect.isclass(getattr(module, name))
                ]
                for cls in classes:
                    if (
                        hasattr(cls, codename)
                        andgetattr(cls, codename).__code__ == f.f_code
                    ):
                        fobj = getattr(cls, codename)
                        breakif fobj isNone:
            """it's likely some nested function/method or a lambda, who logs in a lambda?"""return fobj
    except Exception:
        """never break logging"""

Post a Comment for "Get Function Object From Stack (frame) Object"