I'm trying to decorate all methods in class and i succeded with this code, but i'm also trying to log calls to operators like * + - / , is there any way to decorate them or something like getattr(self,"*") to log the calls ?
class Logger(object):
def __init__(self, bool):
self.bool = bool
def __call__(self, cls):
class DecoratedClass(cls):
def __init__(cls, *args, **kwargs):
super().__init__(*args, **kwargs)
if not(self.bool):
return
methods = [func for func in dir(cls)
if callable(getattr(cls, func))
and not func.startswith("__class")]
for func in methods:
old_func = getattr(cls, func)
def decorated_function(fname, fn):
def loggedFunction(*args, **kwargs):
print("Calling {0} from {3} with params {1} and kwargs {2}".format(fname.upper(), args, kwargs, cls))
return fn(*args, **kwargs)
return loggedFunction
setattr(cls, func, decorated_function(func, old_func))
return DecoratedClass
#Logger(True)
class DummyClass():
def __init__(self,foo):
self.foo = foo
def bar(self):
print(self.foo)
def __mul__(self,other):
print("Hello",other)
if __name__ == '__main__':
a = DummyClass('hola')
a.method()
a.__mul__(a) #this is logged
print(a*a) #this is not logged by decorator
Thanks to Ćukasz, here is a working script.
A difficulty I encountered is to handle multiple instances and avoid to decorate multiple times the same class methods. To handle this problem, I keep track of the decorated class methods (cls.__logged).
Another difficulty is to deal with the magic methods like __setattr__, __getattribute__, __repr__, ... My solution is to ignore them, except for a list that you must define at start (loggable_magic_methods).
from functools import wraps
loggable_magic_methods = ['__mul__',]
def is_magic_method(method):
return method.startswith('__')
class Logger(object):
def __init__(self, bool):
self.bool = bool
def __call__(self, cls):
class LoggedClass(cls):
cls.__logged = []
def __init__(instance, *args, **kwargs):
super().__init__(*args, **kwargs)
if not(self.bool):
return
methods = [funcname for funcname in dir(instance)
if callable(getattr(instance, funcname))
and (funcname in loggable_magic_methods or not is_magic_method(funcname))]
def logged(method):
#wraps(method)
def wrapper(*args, **kwargs):
print (method.__name__, args, kwargs, cls)
return method(*args, **kwargs)
return wrapper
for funcname in methods:
if funcname in cls.__logged:
continue
if is_magic_method(funcname):
setattr(cls, funcname, logged(getattr(cls, funcname)))
cls.__logged.append(funcname)
else:
setattr(instance, funcname, logged(getattr(instance, funcname)))
return LoggedClass
#Logger(True)
class DummyClass():
def __init__(self, foo, coef):
self.foo = foo
self.coef = coef
def bar(self):
print(self.foo)
def __mul__(self, other):
print(self.foo)
print(other.foo)
return self.coef * other.coef
if __name__ == '__main__':
a = DummyClass('hola', 1)
a.bar()
print()
print(a.__mul__(a))
print()
print(a*a)
print()
b = DummyClass('gracias', 2)
b.bar()
print()
print(b.__mul__(a))
print()
print(b*a)
Currently you are patching values on instance. Your usage of cls in __init__ signature is false friend - actually it's old plain self in this case.
If you want to override magic methods, interpreter looks for them on class objects, not on instances.
Minimal example:
class DummyClass:
def __init__(self, foo):
self.foo = foo
def __mul__(self, other):
return self.foo * other.foo
def logged(method):
def wrapper(*args, **kwargs):
print (method.__name__, args, kwargs)
return method(*args, **kwargs)
return wrapper
DummyClass.__mul__ = logged(DummyClass.__mul__)
a = DummyClass(1)
b = DummyClass(2)
assert a * a == 1
assert a * b == 2
assert b * b == 4
Each call is logged.
>>> a = DummyClass(1)
>>> b = DummyClass(2)
>>> assert a * a == 1
__mul__ (<__main__.DummyClass object at 0x00000000011BFEB8>, <__main__.DummyClass object at 0x00000000011BFEB8>) {}
>>> assert a * b == 2
__mul__ (<__main__.DummyClass object at 0x00000000011BFEB8>, <__main__.DummyClass object at 0x00000000011BF080>) {}
>>> assert b * b == 4
__mul__ (<__main__.DummyClass object at 0x00000000011BF080>, <__main__.DummyClass object at 0x00000000011BF080>) {}
I'll leave a task of rewriting monkey-patching approach to you.
Related
I want to wrap a function name funky_the_function with an object which has a __call__ method defined.
Without using functools.wraps the name of the wrapped function will be lost and so will the docstring.
How do I create a meta-class such that instances of the class are wrapped in functools.wraps?
import functools
class MetaDecorator(type):
def __call__(self, *args):
super().__call__(*args)
# SOMEWHERE INSIDE OF `__call__` WE HAVE:
# obj = functools.wraps(obj)
class Decorator(metaclass=MetaDecorator):
def __init__(f):
assert(callable(f))
self._f = f
def __call__(self, *args, **kwargs):
return self._f(*args, **kwargs)
#Decorator
def funky_the_function(*args, **kwargs):
"""Documentation string"""
print('Called example function')
print(funky_the_function.__name__)
print(funky_the_function.__doc__)
1. without metaclass, without wraps:
If you're looking for a way to fix the name and docstring, it can be easily fix by dynamically adding the __name__ and __doc__ to the instance. There is no need for meta-classes.
class Decorator:
def __init__(self, f):
assert callable(f)
self.__name__ = f.__name__
self.__doc__ = f.__doc__
self._f = f
def __call__(self, *args, **kwargs):
return self._f(*args, **kwargs)
2. with metaclass, without wraps:
Of course you can do this in metaclass as well:
class MetaDecorator(type):
def __call__(self, f):
assert callable(f)
instance = super().__call__(f)
instance.__name__ = f.__name__
instance.__doc__ = f.__doc__
return instance
class Decorator(metaclass=MetaDecorator):
def __init__(self, f):
self._f = f
def __call__(self, *args, **kwargs):
return self._f(*args, **kwargs)
3. with metaclass, with wraps:
from functools import wraps
class MetaDecorator(type):
def __call__(self, f):
assert callable(f)
instance = super().__call__(f)
instance = wraps(f)(instance)
return instance
class Decorator(metaclass=MetaDecorator):
def __init__(self, f):
self._f = f
def __call__(self, *args, **kwargs):
return self._f(*args, **kwargs)
I have a decorator to register some class methods. How can I get both self and run parameters correctly?
class Task(object):
_tasks = []
#staticmethod
def register(name):
def decorator(fn):
#wraps(fn)
def wrapper(self=None, run=True, *args, **kwargs):
if not run:
task = defaultdict()
task['name'] = name
task['fn'] = getattr(self, fn.__name__, None)
task['obj'] = self
task['args'] = deepcopy(args)
task['kwargs'] = deepcopy(kwargs)
Task._tasks.append(task)
else:
return fn(self, *args, **kwargs)
return wrapper
return decorator
class Test(object):
def __init__(self, name):
self.name = name
#Task.register('foo')
def foo(self, v1, v2):
print 'running foo in object {} with arguments {} {}'.format(self.name, v1, v2)
#Task.register('hello')
def hello(self):
print 'running hello in object {} '.format(self.name)
def load(self):
self.foo('1', '2', run=False)
self.hello(run=False)
t1=Test('t1')
t1.load()
Traceback (most recent call last):
TypeError: wrapper() got multiple values for keyword argument 'run'
your problem has nothing to do with the decorator. in a simpler form: what you are doing is this:
def foo(run=False, *args, **kwargs):
print(run, args, kwargs)
foo(1, 2, run=True) # TypeError: foo() got multiple values for argument 'run'
from your function signature, python will try to set run=1, args = (2,) and then run into the TypeError.
a fix - though not a very nice one - could be:
def foo(*args, **kwargs):
run = kwargs.pop('run', False) # run defaults to False; remove from kwargs
print(run, args, kwargs)
The run parameter is from the fun, so try to get it from function's parameter:
from collections import defaultdict
from copy import deepcopy
from functools import wraps
class Task(object):
_tasks = []
#staticmethod
def register(name):
def decorator(fn):
#wraps(fn)
def wrapper(self=None, *args, **kwargs):
run = kwargs.pop('run', True)
if not run:
task = defaultdict()
task['name'] = name
task['fn'] = getattr(self, fn.__name__, None)
task['obj'] = self
task['args'] = deepcopy(args)
task['kwargs'] = deepcopy(kwargs)
Task._tasks.append(task)
else:
return fn(self, *args, **kwargs)
return wrapper
return decorator
Python3 seems has better hanlder for the parameters, but don't know how to do this in python2:
from functools import wraps
def optional_debug(func):
#wraps(func)
def wrapper(*args, debug=False, **kwargs):
if debug:
print('Calling', func.__name__)
return func(*args, **kwargs)
return wrapper
#optional_debug
def spam(a,b,c):
print(a,b,c)
spam(1,2,3) # 1,2,3
spam(1,2,3, debug=True) # Calling spam # 1 2 3
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?
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.
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.
You could implement a decorator:
from functools import wraps
def print_function_name(function):
#wraps(function)
def do_it():
print function.__name__
function()
return do_it
Usage:
class MyClass(object):
#print_function_name
def some_function(self):
pass
For example:
>>> my_object = MyClass()
>>> my_object.some_function()
some_function
The use of functools.wraps makes sure the function keeps its documentation and name, instead of becoming do_it.
Taken from https://stackoverflow.com/a/5103895/5270581:
The following method of object class is called on each access to an attribute of an object, including method calls:
__get_attribute__
So I suggest to override it by simply adding a call to a logging function inside.
See https://stackoverflow.com/a/5103895/5270581 (go to last answer) for code example.
This is my answer from this post here
It can be done many different ways. I will show how to make it through meta-class, class decorator and inheritance.
by changing meta class
import functools
class Logger(type):
#staticmethod
def _decorator(fun):
#functools.wraps(fun)
def wrapper(*args, **kwargs):
print(fun.__name__, args, kwargs)
return fun(*args, **kwargs)
return wrapper
def __new__(mcs, name, bases, attrs):
for key in attrs.keys():
if callable(attrs[key]):
# if attrs[key] is callable, then we can easily wrap it with decorator
# and substitute in the future attrs
# only for extra clarity (though it is wider type than function)
fun = attrs[key]
attrs[key] = Logger._decorator(fun)
# and then invoke __new__ in type metaclass
return super().__new__(mcs, name, bases, attrs)
class A(metaclass=Logger):
def __init__(self):
self.some_val = "some_val"
def method_first(self, a, b):
print(a, self.some_val)
def another_method(self, c):
print(c)
#staticmethod
def static_method(d):
print(d)
b = A()
# __init__ (<__main__.A object at 0x7f852a52a2b0>,) {}
b.method_first(5, b="Here should be 5")
# method_first (<__main__.A object at 0x7f852a52a2b0>, 5) {'b': 'Here should be 5'}
# 5 some_val
b.method_first(6, b="Here should be 6")
# method_first (<__main__.A object at 0x7f852a52a2b0>, 6) {'b': 'Here should be 6'}
# 6 some_val
b.another_method(7)
# another_method (<__main__.A object at 0x7f852a52a2b0>, 7) {}
# 7
b.static_method(7)
# 7
Also, will show two approaches how to make it without changing meta information of class (through class decorator and class inheritance). The first approach through class decorator put_decorator_on_all_methods accepts decorator to wrap all member callable objects of class.
def logger(f):
#functools.wraps(f)
def wrapper(*args, **kwargs):
print(f.__name__, args, kwargs)
return f(*args, **kwargs)
return wrapper
def put_decorator_on_all_methods(decorator, cls=None):
if cls is None:
return lambda cls: put_decorator_on_all_methods(decorator, cls)
class Decoratable(cls):
def __init__(self, *args, **kargs):
super().__init__(*args, **kargs)
def __getattribute__(self, item):
value = object.__getattribute__(self, item)
if callable(value):
return decorator(value)
return value
return Decoratable
#put_decorator_on_all_methods(logger)
class A:
def method(self, a, b):
print(a)
def another_method(self, c):
print(c)
#staticmethod
def static_method(d):
print(d)
b = A()
b.method(5, b="Here should be 5")
# >>> method (5,) {'b': 'Here should be 5'}
# >>> 5
b.method(6, b="Here should be 6")
# >>> method (6,) {'b': 'Here should be 6'}
# >>> 6
b.another_method(7)
# >>> another_method (7,) {}
# >>> 7
b.static_method(8)
# >>> static_method (8,) {}
# >>> 8
And, recently, I've come across on the same problem, but I couldn't put decorator on class or change it in any other way, except I was allowed to add such behavior through inheritance only (I am not sure that this is the best choice if you can change codebase as you wish though).
Here class Logger forces all callable members of subclasses to write information about their invocations, see code below.
class Logger:
def _decorator(self, f):
#functools.wraps(f)
def wrapper(*args, **kwargs):
print(f.__name__, args, kwargs)
return f(*args, **kwargs)
return wrapper
def __getattribute__(self, item):
value = object.__getattribute__(self, item)
if callable(value):
decorator = object.__getattribute__(self, '_decorator')
return decorator(value)
return value
class A(Logger):
def method(self, a, b):
print(a)
def another_method(self, c):
print(c)
#staticmethod
def static_method(d):
print(d)
b = A()
b.method(5, b="Here should be 5")
# >>> method (5,) {'b': 'Here should be 5'}
# >>> 5
b.method(6, b="Here should be 6")
# >>> method (6,) {'b': 'Here should be 6'}
# >>> 6
b.another_method(7)
# >>> another_method (7,) {}
# >>> 7
b.static_method(7)
# >>> static_method (7,) {}
# >>> 7
Or more abstractly, you can instantiate base class based on some decorator.
def decorator(f):
#functools.wraps(f)
def wrapper(*args, **kwargs):
print(f.__name__, args, kwargs)
return f(*args, **kwargs)
return wrapper
class Decoratable:
def __init__(self, dec):
self._decorator = dec
def __getattribute__(self, item):
value = object.__getattribute__(self, item)
if callable(value):
decorator = object.__getattribute__(self, '_decorator')
return decorator(value)
return value
class A(Decoratable):
def __init__(self, dec):
super().__init__(dec)
def method(self, a, b):
print(a)
def another_method(self, c):
print(c)
#staticmethod
def static_method(d):
print(d)
b = A(decorator)
b.method(5, b="Here should be 5")
# >>> method (5,) {'b': 'Here should be 5'}
# >>> 5
b.method(6, b="Here should be 6")
# >>> method (6,) {'b': 'Here should be 6'}
# >>> 6
b.another_method(7)
# >>> another_method (7,) {}
# >>> 7
b.static_method(7)
# >>> static_method (7,) {}
# >>> 7
I want to call a wrapper method of function with argument just before I call specific method.
So I guess I have to ovewrite __getattribute__ method.
Here is an example of code:
def wrapper(func):
return func * 2
class A(object):
def test(self, arg):
return arg + 1
def __getattribute__(self, name):
if name in ['test']:
return wrapper(super(A, self).__getattribute__(name))
return super(A, self).__getattribute__(name)
The matter is that getattribute is called when test return the value. What I want is to be able to catch test with the arguments and define the wrapper method like this:
def wrapper(func, *args, **kwargs):
print "do some stuff"
return func(*args, **kwargs)
Use a factory function to return your wrapper:
def decorate(f):
def wrapper(*args, **kw):
return f(*args, **kw) * 2
return wrapper
Here f is closed over by wrapper(), so it can access the name when called.
Then return this in the __getattribute__ hook:
def __getattribute__(self, name):
result = super(A, self).__getattribute__(name)
if name in ('test',):
return decorate(result)
return result
Of course, you could also just apply decorate as a decorator on test then:
class A(object):
#decorate
def test(self, arg):
return arg + 1
If I understand you correctly, you can use a decorator.
def wrapper(func):
def _wrapper(*args, **kwargs):
return func(*args, **kwargs) * 2
return _wrapper
class A(object):
#wrapper
def test(self, arg):
return arg + 1
I am looking for a way to intercept instance method calls in class MyWrapper below:
class SomeClass1:
def a1(self):
self.internal_z()
return "a1"
def a2(self):
return "a2"
def internal_z(self):
return "z"
class SomeClass2(SomeClass1):
pass
class MyWrapper(SomeClass2):
# def INTERCEPT_ALL_FUNCTION_CALLS():
# result = Call_Original_Function()
# self.str += result
# return result
def __init__(self):
self.str = ''
def getFinalResult(self):
return self.str
x = MyWrapper()
x.a1()
x.a2()
I want to intercept all function calls make through my wrapper class. In my wrapper class I want to keep track of all the result strings.
result = x.getFinalResult()
print result == 'a1a2'
Some quick and dirty code:
class Wrapper:
def __init__(self, obj):
self.obj = obj
self.callable_results = []
def __getattr__(self, attr):
print("Getting {0}.{1}".format(type(self.obj).__name__, attr))
ret = getattr(self.obj, attr)
if hasattr(ret, "__call__"):
return self.FunctionWrapper(self, ret)
return ret
class FunctionWrapper:
def __init__(self, parent, callable):
self.parent = parent
self.callable = callable
def __call__(self, *args, **kwargs):
print("Calling {0}.{1}".format(
type(self.parent.obj).__name__, self.callable.__name__))
ret = self.callable(*args, **kwargs)
self.parent.callable_results.append(ret)
return ret
class A:
def __init__(self, val): self.val = val
def getval(self): return self.val
w = Wrapper(A(10))
print(w.val)
w.getval()
print(w.callable_results)
Might not be thorough, but could be a decent starting point, I guess.
You could wrap your methods with decorators a instanciation time:
#!/usr/bin/env python
import inspect
def log(func):
def _logged(*args, **kw):
print "[LD] func", func.__name__, "called with:", args, kw
result = func(*args, **kw)
print "[LD] func", func.__name__, "returned:", result
return result
return _logged
class A(object):
def __init__(self):
for x in inspect.getmembers(self, (inspect.ismethod)):
if not x[0].startswith('__'):
setattr(self, x[0], log(getattr(self, x[0])))
def hello(self):
print "Hello"
def bye(self):
print "Bye"
return 0
Now if you call hello or bye, the call goes through log first:
a = A()
a.hello()
a.bye()
# [LD] func hello called with: () {}
# Hello
# [LD] func hello returned: None
# [LD] func bye called with: () {}
# Bye
# [LD] func bye returned: 0
What you want to do is quite similar to this question.
You should take your example code in the reverse order, i mean creating a class to record return values of method calls, and make the classes you want to watch inherit from it.
Which would give something like this
class RetValWatcher(object):
def __init__(self):
self.retvals = []
def __getattribute__(self, name):
attr = super(RetValWatcher, self).__getattribute__(name)
if callable(attr):
def wrapped(*args, **kwargs):
retval = attr(*args, **kwargs)
self.retvals.append(retval)
return retval
return wrapped
else:
return attr
def getFinalResult(self):
return ''.join(self.retvals)
class MyClass(RetValWatcher):
def a(self):
self.internal_z()
return 'a1'
def b(self):
return 'b1'
def internal_z(self):
return 'z'
x = MyClass()
x.a()
x.b()
print x.getFinalResult()
#'za1b1'
With some minor changes, this method would also allow you to record return values across all RetValWatcher instances.
Edit: added changes suggested by singularity's comment
Edit2: forgot to handle the case where attr is not a method (thx singularity again)
Edit3: fixed typo