0001"""
0002Validator for repeating items.
0003"""
0004
0005from api import NoDefault, Invalid
0006from compound import CompoundValidator, to_python, from_python
0007try:
0008 from sets import Set
0009except ImportError:
0010 # We only use it for type information now:
0011 Set = None
0012
0013__all__ = ['ForEach']
0014
0015class ForEach(CompoundValidator):
0016
0017 """
0018 Use this to apply a validator/converter to each item in a list.
0019
0020 For instance::
0021
0022 ForEach(AsInt(), InList([1, 2, 3]))
0023
0024 Will take a list of values and try to convert each of them to
0025 an integer, and then check if each integer is 1, 2, or 3. Using
0026 multiple arguments is equivalent to::
0027
0028 ForEach(All(AsInt(), InList([1, 2, 3])))
0029
0030 Use convert_to_list=True if you want to force the input to be a
0031 list. This will turn non-lists into one-element lists, and None
0032 into the empty list. This tries to detect sequences by iterating
0033 over them (except strings, which aren't considered sequences).
0034
0035 ForEach will try to convert the entire list, even if errors are
0036 encountered. If errors are encountered, they will be collected
0037 and a single Invalid exception will be raised at the end (with
0038 error_list set).
0039
0040 If the incoming value is a Set, then we return a Set.
0041 """
0042
0043 convert_to_list = True
0044 if_empty = NoDefault
0045 if_missing = []
0046 repeating = True
0047
0048 def attempt_convert(self, value, state, validate):
0049 if self.convert_to_list:
0050 value = self._convert_to_list(value)
0051 if self.if_empty is not NoDefault and not value:
0052 return self.if_empty
0053 if self.not_empty and not value:
0054 if validate is from_python and self.accept_python:
0055 return []
0056 raise Invalid(
0057 self.message('empty', state),
0058 value, state)
0059 new_list = []
0060 errors = []
0061 all_good = True
0062 is_set = isinstance(value, Set)
0063 if state is not None:
0064 previous_index = getattr(state, 'index', NoDefault)
0065 previous_full_list = getattr(state, 'full_list', NoDefault)
0066 index = 0
0067 state.full_list = value
0068 try:
0069 for sub_value in value:
0070 if state:
0071 state.index = index
0072 index += 1
0073 good_pass = True
0074 for validator in self.validators:
0075 try:
0076 sub_value = validate(validator, sub_value, state)
0077 except Invalid, e:
0078 errors.append(e)
0079 all_good = False
0080 good_pass = False
0081 break
0082 if good_pass:
0083 errors.append(None)
0084 new_list.append(sub_value)
0085 if all_good:
0086 if is_set:
0087 new_list = Set(new_list)
0088 return new_list
0089 else:
0090 raise Invalid(
0091 'Errors:\n%s' % '\n'.join([unicode(e) for e in errors if e]),
0092 value,
0093 state,
0094 error_list=errors)
0095 finally:
0096 if state is not None:
0097 if previous_index is NoDefault:
0098 try:
0099 del state.index
0100 except AttributeError:
0101 pass
0102 else:
0103 state.index = previous_index
0104 if previous_full_list is NoDefault:
0105 try:
0106 del state.full_list
0107 except AttributeError:
0108 pass
0109 else:
0110 state.full_list = previous_full_list
0111
0112 def _convert_to_list(self, value):
0113 if isinstance(value, (str, unicode)):
0114 return [value]
0115 elif value is None:
0116 return []
0117 elif isinstance(value, (list, tuple)):
0118 return value
0119 try:
0120 for n in value:
0121 break
0122 return value
0123 ## @@: Should this catch any other errors?:
0124 except TypeError:
0125 return [value]