Decorator for validation - inefficient?
Bryan
bryanvick at gmail.com
Fri Oct 31 14:26:19 EDT 2008
I want my business objects to be able to do this:
class Person(base):
def __init__(self):
self.name = None
@base.validator
def validate_name(self):
if not self.name: return ['Name cannot be empty']
p = Person()
print p.invalid # Prints ['Name cannot be empty']
p.name = 'foo'
print p.invalid # Prints []
print bool(p.invalid) # Prints False
The invalid attribute and validator decorator would be in the base
class:
class base(object):
@staticmethod # so child can say: @base.validator
def validator(func):
"""Mark the function as a validator."""
func._validator = True
return func
def _get_invalid(self):
"""Collect all validation results from registered validators"""
result = []
for attrName in dir(self):
# Prevent recursive calls
if attrName == 'get_invalid' or attrName == 'invalid':
continue
attr = eval('self.' + attrName) # Get attribute
if str(type(attr)) == "<type 'instancemethod'>": # Check
if is function
if hasattr(attr, '_validator'): # Check if function
is a validator
valerr = attr() # Get result of validation
# Validation result can be a single string, list
of strings, or None.
# If the validation fails, it will be a string or
list of strings
# which describe what the validation errors are.
# If the validation succeeds, None is returned.
if type(valerr) == type([]):
for err in valerr:
result.append(err)
else:
if valerr != None: result.append(valerr)
return result # List of validation error strings
invalid = property(_get_invalid, None, None, "List of validation
errors") # Read-only, so no fset or fdel
I don't really like the _get_invalid() logic that reflects over each
attribute and does ugly string comparisons. Is there a cleaner/more
pythonic way to do this reflection?
Also, I am using a decorator to simply mark a function as being a
validator. This works, but I must enumerate all of the object's
attributes to find all the validators. My original plan was to use a
decorator to "register" a function as being a validator. Then the
_get_invalid() call would only need to enumerate the registered
functions. I ran into problems when I couldn't figure out how to use
a decorator to store a function in an attribute of the function's
class:
# Decorator to register function as a validator
def validator(func):
"""Save func in a list of validator functions on the object that
contains func"""
self._validation_functions.append(func) # ERROR: Cannot access
self this way
return func
I appreciate your feedback. I am relatively new to python but have
become completely enamored by it after looking for alternatives to the
MS languages I use to develop business apps.
More information about the Python-list
mailing list