def _validate_fields(schema, value, errors=None): if errors is None: errors = [] # Interface can be used as schema property for Object fields that plan to # hold values of any type. # Because Interface does not include any Attribute, it is obviously not # worth looping on its methods and filter them all out. if schema is Interface: return errors # if `value` is part of a cyclic graph, we need to break the cycle to avoid # infinite recursion. Collect validated objects in a thread local dict by # it's python represenation. A previous version was setting a volatile # attribute which didn't work with security proxy if id(value) in VALIDATED_VALUES: return errors VALIDATED_VALUES[id(value)] = True # (If we have gotten here, we know that `value` provides an interface # other than zope.interface.Interface; # iow, we can rely on the fact that it is an instance # that supports attribute assignment.) try: for name in schema.names(all=True): if not IMethod.providedBy(schema[name]): try: attribute = schema[name] if IChoice.providedBy(attribute): # Choice must be bound before validation otherwise # IContextSourceBinder is not iterable in validation bound = attribute.bind(value) bound.validate(getattr(value, name, None)) elif IField.providedBy(attribute): # validate attributes that are fields attribute.validate(getattr(value, name, None)) except ValidationError as error: errors.append(error) except AttributeError as error: # property for the given name is not implemented errors.append(SchemaNotFullyImplemented(error)) finally: del VALIDATED_VALUES[id(value)] return errors