I am working on finding a way to reduce boilerplate decorators. We have a lot of classes that use a @decorate. For example:
class MyClass(Base):
@decorate
def fun1(self):
pass
@decorate
def fun2(self):
pass
def fun3(self):
pass
I want to make it so by default the decorator is there, unless someone specifies otherwise.
I use this code to do the autowrap
from functools import wraps
def myDecorator(func):
@wraps(func)
def decorator(self, *args, **kwargs):
try:
print 'enter'
ret = func(self, *args, **kwargs)
print 'leave'
except:
print 'exception'
ret = None
return ret
return decorator
class TestDecorateAllMeta(type):
def __new__(cls, name, bases, local):
for attr in local:
value = local[attr]
if callable(value):
local[attr] = myDecorator(value)
return type.__new__(cls, name, bases, local)
class TestClass(object):
__metaclass__ = TestDecorateAllMeta
def test_print2(self, val):
print val
def test_print(self, val):
print val
c = TestClass()
c.test_print1("print 1")
c.test_print2("print 2")
My question are:
Ideally my end solution would be something like:
class TestClass(object):
__metaclass__ = TestDecorateAllMeta
def autowrap(self):
print("Auto wrap")
@dont_decorate
def test_dont_decorate(self, val):
print val
Edit
To speak to one of the comments below, since classess are callable instead of doing
if callable(value):
It should read:
if isinstance(value,types.FunctionType)
Rather than making the user of my class specify a __metaclass__
attribute I would just have them derive from my base class that defines it. No need to expose the plumbing unnecessarily.
Other than that, looks good, though. Your @dont_decorate
function decorator can be implemented by setting an attribute on the original function, which your class decorator then detects and skips the decoration if it is present.
def dont_decorate(func):
func._dont_decorate = True
return func
Then in your metaclass, where you now have the line if callable(value):
just put:
if callable(value) and not hasttr(value, "_dont_decorate"):
As an aside, classes are callable so if you don't want inner classes decorated, you should probably check for functions using isinstance()
rather than callable()
.
If you are interested in more explicit alternatives, you might take a look at this recent question where someone wanted to do essentially the same thing using a class decorator. Unfortunately, this is a good bit more complicated because the methods are already wrapped by the time the decorator sees them.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With