Reputation: 10820
I want to create a decorator for class functions that will produce logging messages at the start and end of the functions with information specific to the class instance running the function. I've tried doing this by creating the decorator as class member, but that doesn't work because the decorator expects the function as the first argument, but as a class method it needs to be self
. Here is what I've written so far that doesn't work:
import functools, logging
logging.basicConfig(format='%(asctime)s - %(levelname)s - %(funcName)s:%(message)s',
datefmt='%Y-%m-%d %I:%M:%S %p',
level=logging.DEBUG)
class Foo:
def __init__(self, bar):
self.bar = bar
def log(func):
@functools.wraps(func)
def wrapper_log(*args, **kwargs):
logging.info(f"Started (instance {self.bar})")
func(*args, **kwargs)
logging.info(f"Finished (instance {self.bar}")
return func(*args, **kwargs)
return wrapper_log
@log
def test(self, a, b, c):
pass
foo = Foo("bar")
foo.test(1, "b", [3, 4])
I can't move it out of the class and pass the class instance as an argument by decorating the class functions with @log(self)
because self
doesn't exist outside of the functions. How can I do this?
Upvotes: 0
Views: 434
Reputation: 359
You can define the decorator outside the class and it will work just fine, but you need to refer explicitly to self
in the wrapper signature.
The reason why this work is because all the definitions inside a function are evaluated only when the function is called. By the time the function test
is called it has already been bounded to the instance and self
will exists in its namespace.
import functools, logging
logging.basicConfig(format='%(asctime)s - %(levelname)s - %(funcName)s:%(message)s',
datefmt='%Y-%m-%d %I:%M:%S %p',
level=logging.DEBUG)
def log(func):
@functools.wraps(func)
def wrapper_log(self, *args, **kwargs):
logging.info(f"Started (instance {self.bar})")
func(self, *args, **kwargs)
logging.info(f"Finished (instance {self.bar}")
return func(self, *args, **kwargs)
return wrapper_log
class Foo:
def __init__(self, bar):
self.bar = bar
@log
def test(self, a, b, c):
pass
foo = Foo("bar")
foo.test(1, "b", [3, 4])
This will output
2020-10-05 11:31:20 PM - INFO - wrapper_log:Started (instance bar)
2020-10-05 11:31:20 PM - INFO - wrapper_log:Finished (instance bar
Upvotes: 2