Using a decorator to add a return of an inner function with an added property
I have a large number of functions that are executed as tests when trying to save an object, so I am creating a way to make it more convenient to register these methods as constraints (tests) on the object.
I am confused about using decorators in my solution. What I was going to do:
- Create a decorator adding a property to a method on an object
- Decorate the techniques that I want to mark as constraints.
- End all methods of my object and call execute whatever the flag has
Using a decorator doesn't work, but adding a flag by my author does.
Here is the code:
from functools import wrap
def constraint(func):
@wraps(func)
def inner(*args, **kwargs):
func._is_constraint = True # Here after returning the function I add a flag
return func(*args, **kwargs)
return inner
class Record(object):
param1 = 100 # some defaults
param2 = 20
@constraint # This dont works
def check_test1(self):
if param1 < 0:
return 'No value less than zero'
# This works fine
def check_test2(self):
if param2 < 0:
return 'No value less than zero'
check_test2._is_constraint = True
def a_random_method(self):
print 'some random thing'
So I am trying like:
>>> rec = Record()
>>> rec.param1 = -100
>>> rec.param2 = -100
>>> for prop in dir(rec):
... if hasattr(getattr(rec, prop), '_is_constraint'):
... constraint = getattr(rec, prop)
... print prop, constraint()
...
'check_param2: No value less than zero'
>>>
See check_param1 failed.
So how can I get it to work with decorators? possibly?
I am trying to use in this particular case https://gist.github.com/mariocesar/4684561
source to share
Ok, the first thing I see is you want to add a parameter to the function object in the decorator, not a closure; the closure is called when the function is called from the decorated scope, so marking it in the closure does this after the fact:
def constraint(func):
func._is_constraint = True
@wraps(func)
def inner(*args, **kwargs):
return func(*args, **kwargs)
return inner
Also, if all you're doing is directly calling func on the closure without adding any functionality, just flagging it, the closure is completely redundant:
def constraint(func):
func._is_constraint = True
return func
In this case, you can be better served by a different pattern:
class ConstraintsType(type):
def __new__(cls, name, bases, attrs):
attrs['constraint_names'] = []
for attr in attrs:
if attr.startswith('constraint_'):
attrs['constraint_names'].append(attr)
return super(ConstraintsType, cls).__new__(cls, name, bases, attrs)
class Constraints(object):
__metaclass__ = ConstraintsType
@property
def constraints(self):
for name in self.constraint_names:
yield getattr(self, name)
class Record(Constraints):
def __init__(self, params=(100, 20)):
self.params = params
def constraint_greater_than_0(self, value):
return value > 0
def run(self):
for index, value in enumerate(self.params):
for func in self.constraints:
if not func(value):
print 'param %d (%s) did not satisfy constraint %s' % (index, value, func.__name__)
Record().run()
for value_set in ((-100, -100), (0, 0), (-1,1), (1,-1)):
Record(value_set).run()
source to share