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