formencode.foreach
Covered: 48 lines
Missed: 82 lines
Skipped 19 lines
Percent: 36 %
  1
"""
  2
Validator for repeating items.
  3
"""
  4
import warnings
  5
filters = warnings.filters[:]
  6
warnings.simplefilter('ignore', DeprecationWarning)
  7
from sets import Set
  8
warnings.filters = filters
  9
try:
 10
    set
 11
except NameError:
 12
    set = Set
 14
from api import NoDefault, Invalid
 15
from compound import CompoundValidator, to_python, from_python
 17
__all__ = ['ForEach']
 19
class ForEach(CompoundValidator):
 21
    """
 22
    Use this to apply a validator/converter to each item in a list.
 24
    For instance::
 26
        ForEach(AsInt(), InList([1, 2, 3]))
 28
    Will take a list of values and try to convert each of them to
 29
    an integer, and then check if each integer is 1, 2, or 3.  Using
 30
    multiple arguments is equivalent to::
 32
        ForEach(All(AsInt(), InList([1, 2, 3])))
 34
    Use convert_to_list=True if you want to force the input to be a
 35
    list.  This will turn non-lists into one-element lists, and None
 36
    into the empty list.  This tries to detect sequences by iterating
 37
    over them (except strings, which aren't considered sequences).
 39
    ForEach will try to convert the entire list, even if errors are
 40
    encountered.  If errors are encountered, they will be collected
 41
    and a single Invalid exception will be raised at the end (with
 42
    error_list set).
 44
    If the incoming value is a set, then we return a set.
 45
    """
 47
    convert_to_list = True
 48
    if_empty = NoDefault
 49
    repeating = True
 50
    _if_missing = ()
 52
    def attempt_convert(self, value, state, validate):
 53
        if self.convert_to_list:
 54
            value = self._convert_to_list(value)
 55
        if self.if_empty is not NoDefault and not value:
 56
            return self.if_empty
 57
        if self.not_empty and not value:
 58
            if validate is from_python and self.accept_python:
 59
                return []
 60
            raise Invalid(
 61
                self.message('empty', state),
 62
                value, state)
 63
        new_list = []
 64
        errors = []
 65
        all_good = True
 66
        is_set = isinstance(value, (set, Set))
 67
        if state is not None:
 68
            previous_index = getattr(state, 'index', NoDefault)
 69
            previous_full_list = getattr(state, 'full_list', NoDefault)
 70
            index = 0
 71
            state.full_list = value
 72
        try:
 73
            for sub_value in value:
 74
                if state:
 75
                    state.index = index
 76
                    index += 1
 77
                good_pass = True
 78
                for validator in self.validators:
 79
                    try:
 80
                        sub_value = validate(validator, sub_value, state)
 81
                    except Invalid, e:
 82
                        errors.append(e)
 83
                        all_good = False
 84
                        good_pass = False
 85
                        break
 86
                if good_pass:
 87
                    errors.append(None)
 88
                new_list.append(sub_value)
 89
            if all_good:
 90
                if is_set:
 91
                    new_list = set(new_list)
 92
                return new_list
 93
            else:
 94
                raise Invalid(
 95
                    'Errors:\n%s' % '\n'.join([unicode(e) for e in errors if e]),
 96
                    value,
 97
                    state,
 98
                    error_list=errors)
 99
        finally:
100
            if state is not None:
101
                if previous_index is NoDefault:
102
                    try:
103
                        del state.index
104
                    except AttributeError:
105
                        pass
106
                else:
107
                    state.index = previous_index
108
                if previous_full_list is NoDefault:
109
                    try:
110
                        del state.full_list
111
                    except AttributeError:
112
                        pass
113
                else:
114
                    state.full_list = previous_full_list
116
    def empty_value(self, value):
117
        return []
119
    class _IfMissing(object):
120
        def __get__(self, obj, type=None):
121
            if obj is None:
122
                return []
123
            elif obj._if_missing is ForEach._if_missing:
124
                return []
125
            else:
126
                return obj._if_missing
127
        def __set__(self, obj, value):
128
            obj._if_missing = value
129
        def __delete__(self, obj):
130
            obj._if_missing = NoDefault
132
    if_missing = _IfMissing()
133
    del _IfMissing
135
    def _convert_to_list(self, value):
136
        if isinstance(value, (str, unicode)):
137
            return [value]
138
        elif value is None:
139
            return []
140
        elif isinstance(value, (list, tuple)):
141
            return value
142
        try:
143
            for n in value:
144
                break
145
            return value
147
        except TypeError:
148
            return [value]