I want to wrap every method of various objects except __init__
using a decorator.
class MyObject(object):
def method(self):
print "method called on %s" % str(self)
@property
def result(self):
return "Some derived property"
def my_decorator(func):
def _wrapped(*args, **kwargs):
print "Calling decorated function %s" % func
return func(*args, **kwargs)
return _wrapped
class WrappedObject(object):
def __init__(self, cls):
for attr, item in cls.__dict__.items():
if attr != '__init__' and (callable(item) or isinstance(item, property)):
setattr(cls, attr, my_decorator(item))
self._cls = cls
def __call__(self, *args, **kwargs):
return self._cls(*args, **kwargs)
inst = WrappedObject(MyObject)()
However, the wrapping of a property instance results is equivalent to this:
@my_decorator
@property
def result(self):
return "Some derived property"
When the desired result is something equivalent to this:
@property
@my_decorator
def result(self):
return "Some derived property"
It seems the attributes of a property object are read-only preventing modifying the function after property has wrapped it. I'm not too comfortable with the level of hackery required already and I'd rather not delve into the property object anyway.
The only other solution I can see is to generate a metaclass on the fly which I was hoping to avoid. Am I missing something obvious?
You could introduce "lazy" decorators, which are applied after your own decorator, for example:
..and then use
@lazy_property
instead of@property
. (Warning: untested code, but I hope you get the idea...)After a bit of try-and-error, I came up with the following solution. First, create a helper class that will emulate a decorated descriptor:
Then, if you see a property, wrap it in it:
This works like this:
There! No metaclasses :)
There are a few other issues in this sample, but to atain to question, all you have to do is, when you are wrapping a property
When you are wrapping a property, wrap its __get__ method instead:
That is the simpelst modification to your code that does the job. I'd however change it to dinamically a subclass of the classs it is wrapping, in order to avoid re-writing its attributes. You can create a subclass programtically by simply caling type with the name, a tuple withe the bases, and a dict as parameters.
edit - changing code to subclass wrapped class
Actually, subclassing the given class requires almost no modification on the given code, but for the
type
call I indicated. I just tested it here - change your WrappedObject class to: