On Oct 31, 6:26 pm, Bryan <[EMAIL PROTECTED]> wrote: > 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.
Hi I suggest a simpler approach class Base(object): @property def invalid(self): invalid = [] for field in self.fields: validate_field = getattr(self, 'validate_' + field) if validate_field: errors = validate_field() if errors: invalid.extend(errors) return invalid class Person(Base): fields = 'name', 'age' def __init__(self): self.name = None self.age = None def validate_name(self): if not self.name: return ['Name cannot be empty'] def validate_age(self): if self.age is None: return ['Age cannot be empty'] if not isinstance(self.age, int): return ['Age must be a number'] Then: >>> p=Person() >>> p.invalid ['Name cannot be empty', 'Age cannot be empty'] >>> p.name='Lorenzo' >>> p.invalid ['Age cannot be empty'] >>> p.age='green' >>> p.invalid ['Age must be a number'] >>> p.age=12 >>> p.invalid [] >>> HTH -- Arnaud -- http://mail.python.org/mailman/listinfo/python-list