Example #1
0
    def load(self, data, *args, **kwargs):
        if data is MISSING or data is None:
            self._fail('required')

        if not is_dict(data):
            self._fail('invalid')

        errors_builder = ValidationErrorBuilder()
        result = {}

        for name, field in iteritems(self.fields):
            try:
                loaded = field.load(name, data, *args, **kwargs)
                if loaded != MISSING:
                    result[name] = loaded
            except ValidationError as ve:
                errors_builder.add_error(name, ve.messages)

        if self.allow_extra_fields is False:
            field_names = [name for name, _ in iteritems(self.fields)]
            for name in data:
                if name not in field_names:
                    errors_builder.add_error(name, self._error_messages['unknown'])

        errors_builder.raise_errors()

        result = super(Object, self).load(result, *args, **kwargs)
        if self.constructor:
            result = self.constructor(**result)
        return result
Example #2
0
    def test_iteritems(self):
        assert sorted(iteritems(OpenStruct())) == []

        o = OpenStruct({'foo': 'hello'})
        assert sorted(iteritems(o)) == sorted(o.items())

        o['bar'] = 123
        assert sorted(iteritems(o)) == sorted(o.items())
Example #3
0
 def __repr__(self):
     return '<{klass} {properties}>'.format(
         klass= self.__class__.__name__,
         properties=' '.join(['%s=%s' % (k, repr(v))
                              for k, v in iteritems({'min': self.min,
                                                     'max': self.max})
                              if v is not None])
     )
Example #4
0
 def __repr__(self):
     return '<{klass} {properties}>'.format(
         klass=self.__class__.__name__,
         properties=' '.join([
             '%s=%s' % (k, repr(v)) for k, v in iteritems({
                 'min': self.min,
                 'max': self.max
             }) if v is not None
         ]))
Example #5
0
def merge_errors(errors1, errors2):
    """Deeply merges two error messages. Error messages can be
    string, list of strings or dict of error messages (recursively).
    Format is the same as accepted by :exc:`ValidationError`.
    Returns new error messages.
    """
    if errors1 is None:
        return errors2
    elif errors2 is None:
        return errors1

    if isinstance(errors1, list):
        if not errors1:
            return errors2

        if isinstance(errors2, list):
            return errors1 + errors2
        elif isinstance(errors2, dict):
            return dict(
                errors2,
                **{SCHEMA: merge_errors(errors1, errors2.get(SCHEMA))}
            )
        else:
            return errors1 + [errors2]
    elif isinstance(errors1, dict):
        if isinstance(errors2, list):
            return dict(
                errors1,
                **{SCHEMA: merge_errors(errors1.get(SCHEMA), errors2)}
            )
        elif isinstance(errors2, dict):
            errors = dict(errors1)
            for k, v in iteritems(errors2):
                if k in errors:
                    errors[k] = merge_errors(errors[k], v)
                else:
                    errors[k] = v
            return errors
        else:
            return dict(
                errors1,
                **{SCHEMA: merge_errors(errors1.get(SCHEMA), errors2)}
            )
    else:
        if isinstance(errors2, list):
            return [errors1] + errors2 if errors2 else errors1
        elif isinstance(errors2, dict):
            return dict(
                errors2,
                **{SCHEMA: merge_errors(errors1, errors2.get(SCHEMA))}
            )
        else:
            return [errors1, errors2]
Example #6
0
    def _resolve_fields(self, bases, fields, only=None, exclude=None):
        all_fields = []
        if bases is not None:
            for base in bases:
                all_fields += list(iteritems(base.fields))

        if only is not None:
            all_fields = [(name, field)
                          for name, field in all_fields
                          if name in only]

        if exclude is not None:
            all_fields = [(name, field)
                          for name, field in all_fields
                          if name not in exclude]

        if fields is not None:
            all_fields += [
                (name, self._normalize_field(field))
                for name, field in (iteritems(fields) if is_dict(fields) else fields)
            ]

        return OrderedDict(all_fields)
Example #7
0
    def dump(self, obj, *args, **kwargs):
        if obj is MISSING or obj is None:
            self._fail('required')

        errors_builder = ValidationErrorBuilder()
        result = OrderedDict() if self.ordered else {}

        for name, field in iteritems(self.fields):
            try:
                dumped = field.dump(name, obj, *args, **kwargs)
                if dumped != MISSING:
                    result[name] = dumped
            except ValidationError as ve:
                errors_builder.add_error(name, ve.messages)
        errors_builder.raise_errors()

        return super(Object, self).dump(result, *args, **kwargs)
Example #8
0
    def dump(self, value, *args, **kwargs):
        if value is MISSING or value is None:
            self._fail('required')

        if not is_dict(value):
            self._fail('invalid')

        errors_builder = ValidationErrorBuilder()
        result = {}
        for k, v in iteritems(value):
            value_type = self.value_types.get(k)
            if value_type is None:
                continue
            try:
                result[k] = value_type.dump(v, *args, **kwargs)
            except ValidationError as ve:
                errors_builder.add_error(k, ve.messages)
        errors_builder.raise_errors()

        return super(Dict, self).dump(result, *args, **kwargs)
Example #9
0
    def load_into(self, obj, data, inplace=True, *args, **kwargs):
        """Load data and update existing object.

        :param obj: Object to update with deserialized data.
        :param data: Raw data to get value to deserialize from.
        :param bool inplace: If True update data inplace;
            otherwise - create new data.
        :param kwargs: Same keyword arguments as for :meth:`Type.load`.
        :returns: Updated object.
        :raises: :exc:`~lollipop.errors.ValidationError`
        """
        if obj is None:
            raise ValueError('Load target should not be None')

        if data is MISSING:
            return

        if data is None:
            self._fail('required')

        if not is_dict(data):
            self._fail('invalid')

        errors_builder = ValidationErrorBuilder()

        data1 = {}
        for name, field in iteritems(self.fields):
            try:
                if name in data:
                    # Load new data
                    value = field.load_into(obj, name, data,
                                            inplace=not self.immutable and inplace)
                else:
                    # Retrive data from existing object
                    value = field.get_value(name, obj, *args, **kwargs)

                if value is not MISSING:
                    data1[name] = value
            except ValidationError as ve:
                errors_builder.add_error(name, ve.messages)

        if self.allow_extra_fields is False:
            field_names = [name for name, _ in iteritems(self.fields)]
            for name in data:
                if name not in field_names:
                    errors_builder.add_error(name, self._error_messages['unknown'])

        errors_builder.raise_errors()

        data2 = super(Object, self).load(data1, *args, **kwargs)

        if self.immutable or not inplace:
            result = data2
            if self.constructor:
                result = self.constructor(**result)
        else:
            for name, field in iteritems(self.fields):
                if name not in data:
                    continue

                try:
                    field.set_value(name, obj, data2.get(name, MISSING))
                except ValidationError as ve:
                    raise ValidationError({name: ve.messages})

            result = obj

        return result