Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python - Print method name whenever the methods of a class is called

I need to perform certain operations everytime the methods of a particular class is called (for example log the method name). How can this be achieved in Python in a generic way?

like image 784
Vivek S Avatar asked Aug 27 '15 08:08

Vivek S


2 Answers

Decorate callable attributes from within a metaclass:

from functools import wraps

def _log_method(val):
    @wraps(val)
    def wrapper(*a, **ka):
        print(val.__name__, 'is called')
        val(*a, **ka)
    return wrapper

class LogMethodCalls(type):
    def __new__(cls, cls_name, bases, attrs):
        for name, attr in attrs.items():
            if callable(attr):
                attrs[name] = _log_method(attr)
        return type.__new__(cls, cls_name, bases, attrs)

class Foo(metaclass=LogMethodCalls):
    def my_method(self):
        pass

Foo().my_method() # my_method is called

Warning: This code only works for instance methods, methods that were decorated with @classmethod or @staticmethod will not be logged (because classmethod and staticmethod objects are not callable - they're just non-data descriptors).


The following works for class methods and static methods as well:

from functools import wraps

def _log_method(val):
    @wraps(val)
    def wrapper(*a, **ka):
        print('calling', val.__name__)
        val(*a, **ka)
    return wrapper

class LogMethodCalls(type):
    def __new__(cls, cls_name, bases, attrs):
        for name, attr in attrs.items():
            if callable(attr):
                attrs[name] = _log_method(attr)
            elif isinstance(attr, (classmethod, staticmethod)):
                attrs[name] = type(attr)(_log_method(attr.__func__))
        return type.__new__(cls, cls_name, bases, attrs)

class Foo(metaclass=LogMethodCalls):

    def my_instance_method(self):
        pass

    @classmethod
    def my_class_method(cls):
        pass

    @staticmethod
    def my_static_method():
        pass

Foo().my_instance_method() # calling my_instance_method
Foo.my_class_method() # calling my_class_method
Foo.my_static_method() # calling my_static_method

They have __func__ attributes that we can decorate.


Note that you'll need to use

class Foo(object):
    __metaclass__ = LogMethodCalls

in Python 2.

like image 119
vaultah Avatar answered Oct 19 '22 02:10

vaultah


Taken from this answer. You can use the inspect module to look at the stack for the function name to create a simple logging function. Seems like kind of a hack, but I suppose it answers the question.

import inspect

def log_call():
    print(inspect.stack()[1][3])

def my_func():
    log_call()
    # do stuff

my_func()

This will print my_func.

like image 38
Parker Hoyes Avatar answered Oct 19 '22 02:10

Parker Hoyes