def __init__(self, form, eid, options, label=NotGiven, vtype=NotGiven, defaultval=NotGiven, strip=True, choose='Choose:', auto_validate=True, invalid=[], error_msg=None, required=False, **kwargs): self.multiple = bool(kwargs.pop('multiple', False)) FormFieldElementBase.__init__(self, form, eid, label, vtype, defaultval, strip, required=required, **kwargs) self.options = options self.choose = None if choose: if isinstance(choose, list): self.choose = choose else: self.choose = [(-2, choose), (-1, '-' * 25)] if required: invalid = [-2, -1] + tolist(invalid) self.options = self.choose + options if auto_validate: choose_as_none = [cv[0] for cv in tolist(self.choose)] if required: self.add_processor(Select(self.options, invalid, choose_as_none), error_msg) else: # NotGiven is a valid option as long as a value isn't required ok_values = self.options + [(NotGiven, 0)] + [(NotGivenIter, 0)] self.add_processor(Select(ok_values, invalid, choose_as_none), error_msg)
def validate_other(self, values, state): soptions = set([six.text_type(d[0] if isinstance(d, tuple) else d) for d in self.options]) sinvalid = set([six.text_type(d) for d in tolist(self.invalid)]) svalues = set([six.text_type(d) for d in tolist(values)]) if len(sinvalid.intersection(svalues)) != 0: raise Invalid(self.message('invalid', state), values, state) if len(soptions.intersection(svalues)) != len(svalues): raise Invalid(self.message('notthere', state), values, state) return
def _to_python_processing(self): """ if "choose" value was chosen, we need to return an emtpy value appropriate to `multi` """ FormFieldElementBase._to_python_processing(self) # multiple select fields should always return a list if self.multiple and not is_notgiven(self._safeval): self._safeval = tolist(self._safeval)
def _to_python(self, value, state): valiter = tolist(value) as_empty = [str(d) for d in tolist(self.as_empty)] vallist = [str(d) for d in valiter] # single if len(vallist) == 1: if vallist[0] in as_empty: return None return value # multiple to_remove = [] for index, val in enumerate(vallist): if val in as_empty: to_remove.append(index) adjust = 0 for index in to_remove: del valiter[index - adjust] adjust += 1 return valiter
def _set_members(self, values): # convert to dict with unicode keys so our comparisons are always # the same type values = dict([(str(v), 1) for v in tolist(values)]) # based on our values, set our members to chosen or not chosen for key, el in self.members.items(): if str(key) in values: el.chosen = True else: el.chosen = False
def render_html(self): def option_tag(opt): if isinstance(opt, (list, tuple)): value, label = opt else: value = label = opt return tags.Option(label, str(value)) displayval = self.displayval if self.displayval or self.displayval == 0 else None displayval = [str(val) for val in tolist(displayval)] options = [option_tag(opt) for opt in self.options] return tags.select(self.nameattr or self.id, displayval, options, **self.attributes)
def _to_python(self, value, state): field = state multiple = getattr(field, 'multiple', False) if self.multi_check: if not multiple: if is_iterable(value): raise Invalid(self.message('nonmultiple', state), value, state) # now apply the validator to the value if not multiple or is_notgiven(value) or \ getattr(self.validator, 'handles_multiples', False): return self.validator.to_python(value, state) else: retval = [] for v in tolist(value): retval.append(self.validator.to_python(v, state)) return retval
def render_static(self): if self.displayval == '': todisplay = literal(' ') else: values = [] def mapf(option): if isinstance(option, tuple): return str(option[0]), option[1] else: return str(option), option lookup = dict(map(mapf, self.options)) for key in tolist(self.displayval): try: values.append(lookup[str(key)]) except KeyError: pass todisplay = ', '.join(values) self.add_attr('class', 'select') return HTML.span(todisplay, **self._static_attributes())
def _to_python_processing(self): # noqa """ filters, validates, and converts the submitted value based on element settings and processors """ # if the value has already been processed, don't process it again if self._valid is not None: return valid = True value = self.submittedval # strip if necessary if self.strip and isinstance(value, str): value = value.strip() elif self.strip and is_iterable(value): newvalue = [] for item in value: if isinstance(item, str): newvalue.append(item.strip()) else: newvalue.append(item) if newvalue: value = newvalue # if nothing was submitted, but we have an if_missing, substitute if is_notgiven(value) and self.if_missing is not NotGiven: value = self.if_missing # handle empty or missing submit value with if_empty if is_empty(value) and self.if_empty is not NotGiven: value = self.if_empty # standardize all empty values as None if if_empty not given elif is_empty(value) and not is_notgiven(value): value = None # process required if self.required and self.required_empty_test(value): valid = False self.add_error('field is required') # process processors for processor, msg in self.processors: try: processor = MultiValues(processor) ap_value = processor.to_python(value, self) # FormEncode takes "empty" values and returns None # Since NotGiven == '', FormEncode thinks its empty # and returns None on us. We override that here. if ap_value is not None or value is not NotGiven: value = ap_value except formencode.Invalid as e: valid = False self.add_error((msg or str(e))) else: # we rely on MultiValues for this, but if no processor, # it doesn't get called if getattr(self, 'multiple', False) and not is_iterable(value): value = tolist(value) ### # Doing these again in case the processors changed the value ### # handle empty or missing submit value with if_empty if is_empty(value) and self.if_empty is not NotGiven: value = self.if_empty # standardize all empty values as None if if_empty not given elif is_empty(value) and not is_notgiven(value): value = None # process required if self.required and self.required_empty_test(value) and \ 'field is required' not in self.errors: valid = False self.add_error('field is required') # If its empty, there is no reason to run the converters. By default, # the validators don't do anything if the value is empty and they WILL # try to convert our NotGiven value, which we want to avoid. Therefore, # just skip the conversion. if not is_empty(value): # process type conversion if self.vtype is not NotGiven: if self.vtype in ('boolean', 'bool'): tvalidator = formencode.compound.Any(fev.Bool(), fev.StringBool()) elif self.vtype in ('integer', 'int'): tvalidator = fev.Int elif self.vtype in ('number', 'num', 'float'): tvalidator = fev.Number elif self.vtype in ('decimal'): tvalidator = Decimal elif self.vtype in ('str', 'string'): tvalidator = fev.String elif self.vtype in ('unicode', 'uni'): tvalidator = fev.UnicodeString try: tvalidator = MultiValues(tvalidator, multi_check=False) value = tvalidator.to_python(value, self) except formencode.Invalid as e: valid = False self.add_error(str(e)) # save if valid: self._safeval = value self._valid = True else: # is if_invalid if applicable if self.if_invalid is not NotGiven: # we might want to clear error messages too, but the extra # detail probably won't hurt (for now) self._safeval = self.if_invalid self._valid = True else: self._valid = False
def test_tolist_nonmutable_default(self): x = tolist(None) assert x == [] x.append(3) assert tolist(None) == []
def test_tolist(self): assert tolist([1, 2]) == [1, 2] assert tolist((1, 2)) == [1, 2] assert tolist(1) == [1] assert tolist(None) == [] assert tolist(None, [1, 2]) == [1, 2]