Ejemplo n.º 1
0
    def sanitize_optionals(self, data, schema, tree):
        schema_key_map = {}
        try:
            for number, value in schema.items():
                schema_key_map[number] = getattr(value[0], '_object', value[0])
        except AttributeError:  # maybe not a dict?
            self.length_equality(data, schema, 0, tree)

        optional_keys = {}
        for k, v in schema.items():
            try:
                key = getattr(v[0], '_object')
                if key:
                    optional_keys[k] = key
            except AttributeError:
                pass

        data_keys = [v[0] for k, v in data.items()]

        for number, value in optional_keys.items():
            if value not in data_keys:
                del schema[number]
        if not schema and is_not_empty(data):
            msg = "unexpected extra items"
            raise Invalid(schema, tree, reason=msg)
        return re_sort(schema)
Ejemplo n.º 2
0
def dictionary(_object, *args):
    """
    Validates a given input is of type dictionary.

    Example usage::

        data = {'a' : {'b': 1}}
        schema = ('a', dictionary)

    You can also use this as a decorator, as a way to check for the
    input before it even hits a validator you may be writing.

    .. note::
        If the argument is a callable, the decorating behavior will be
        triggered, otherwise it will act as a normal function.

    """
    error_msg = 'not of type dictionary'
    if is_callable(_object):
        _validator = _object

        @wraps(_validator)
        def decorated(value):
            ensure(isinstance(value, dict), error_msg)
            return _validator(value)
        return decorated
    try:
        ensure(isinstance(_object, dict), error_msg)
    except AssertionError:
        if args:
            msg = 'did not pass validation against callable: dictionary'
            raise Invalid('', msg=msg, reason=error_msg, *args)
        raise
Ejemplo n.º 3
0
    def enforce(self, data, schema, item_index, tree):
        # yo dawg, a recursive validator within a recursive validator anyone?
        if is_callable(schema) and hasattr(schema, '__validator_leaf__'):
            return schema(data[item_index], tree)
        if isinstance(data[item_index], dict) and isinstance(schema, tuple):
            try:
                _validator = Validator(data[item_index], schema)
                _validator.validate()
            except Invalid:
                e = sys.exc_info()[1]
                tree.append('list[%s]' % item_index)
                tree.extend(e.path)
                raise Invalid(e.schema_item,
                              tree,
                              reason=e._reason,
                              pair='value')

            # FIXME this is utterly redundant, and also happens in
            # RecursiveValidator
            except SchemaError:
                e = sys.exc_info()[1]
                tree.extend(e.path)
                raise SchemaError('', tree, reason=e._reason, pair='value')

        elif isinstance(schema,
                        tuple) and not isinstance(data[item_index],
                                                  (tuple, dict)):
            raise SchemaError(
                data,
                tree,
                reason='iterable contains single items, schema does not')
        else:
            try:
                if is_callable(schema):
                    schema(data[item_index])
                else:
                    ensure(data[item_index] == schema)
            except AssertionError:
                reason = sys.exc_info()[1]
                tree.append('list[%s]' % item_index)
                raise Invalid(schema, tree, reason=reason, pair='item')
Ejemplo n.º 4
0
def enforce(data_item, schema_item, tree, pair):
    schema_is_optional = hasattr(schema_item, 'is_optional')
    if is_callable(schema_item) and not schema_is_optional:
        try:
            schema_item(data_item)
        except AssertionError:
            e = sys.exc_info()[1]
            if pair == 'value':
                tree.append(data_item)
            raise Invalid(schema_item, tree, reason=e, pair=pair)
    else:
        try:
            if schema_is_optional:
                if is_empty(data_item):  # we received nothing here
                    return
                ensure(data_item == schema_item())
            else:
                ensure(data_item == schema_item)
        except AssertionError:
            e = sys.exc_info()[1]
            if pair == 'value':
                tree.append(data_item)
            raise Invalid(schema_item, tree, reason=e, pair=pair)
Ejemplo n.º 5
0
    def __call__(self, data, tree):
        schema = expand_schema(self.schema)
        index = len(data) - 1
        validator = RecursiveValidator(data, schema, [], index=index)
        for item_index in range(len(data)):
            try:
                return validator.leaf(item_index)
            except Invalid:
                if tree:
                    tree.pop
                pass

        msg = "did not contain any valid objects against callable: %s" % self.__class__.__name__
        raise Invalid(schema, tree, pair='value', msg=msg)
Ejemplo n.º 6
0
 def safe_type(self, data, tree):
     """
     Make sure that the incoming data complies with the class type we
     are expecting it to be. In this case, classes that inherit from this
     base class expect data to be of type ``list``.
     """
     if not isinstance(data, list):
         name = self.__class__.__name__
         msg = "did not pass validation against callable: %s" % name
         reason = 'expected a list but got %s' % safe_repr(data)
         raise Invalid(self.schema,
                       tree,
                       reason=reason,
                       pair='value',
                       msg=msg)
Ejemplo n.º 7
0
 def enforce(self, data, schema, item_index, tree):
     # yo dawg, a recursive validator within a recursive validator anyone?
     if is_callable(schema) and hasattr(schema, '__validator_leaf__'):
         return schema(data, tree)
     try:
         _validate = Validator({}, self.schema)
         _validate.data = {0: data[item_index]}
         _validate.validate()
     except Invalid:
         e = sys.exc_info()[1]
         tree.extend(e.path)
         raise Invalid(e.schema_item,
                       tree,
                       pair='value',
                       msg=e._msg,
                       reason=e._reason)
     except SchemaError:
         e = sys.exc_info()[1]
         tree.extend(e.path)
         raise SchemaError('', tree, reason=e._reason, pair='value')
Ejemplo n.º 8
0
    def __call__(self, data, tree):
        schema = expand_schema(self.schema)
        self.safe_type(data, tree)
        index = len(data) - 1
        validator = IterableValidator(data,
                                      schema, [],
                                      index=index,
                                      name='AnyItem')
        for item_index in range(len(data)):
            try:
                return validator.leaf(item_index)
            except Invalid:
                pass

        tree.append('list[]')
        if is_callable(schema):
            msg = "did not contain any valid items against callable: %s" % schema.__name__
        else:
            msg = "did not contain any valid items matching %s" % repr(schema)
        raise Invalid(schema, tree, pair='value', msg=msg)
Ejemplo n.º 9
0
def devices_object(_object, *args):
    error_msg = 'not of type dictionary or list'
    if isinstance(_object, dict):
        v = recursive.AllObjects((types.string, types.string))
        # this is truly unfortunate but we don't have access to the 'tree' here
        # (the tree is the path to get to the failing key. We settle by just being
        # able to report nicely.
        v(_object, [])
        return

    try:
        assert isinstance(_object, list)
    except AssertionError:
        if args:
            raise Invalid('dict type',
                          pair='value',
                          msg=None,
                          reason=error_msg,
                          *args)
        raise
Ejemplo n.º 10
0
    def length_equality(self, data, schema, index, tree):
        try:
            data = data[index]
            try:
                schema = schema[index]
            except KeyError:
                if not hasattr(schema, 'must_validate'):
                    reason = 'has unexpected item in data: %s' % data_item(
                        data)
                    raise Invalid(None,
                                  tree,
                                  msg=reason,
                                  reason=reason,
                                  pair='value')
        except (KeyError, TypeError):
            if not hasattr(schema, 'must_validate'):
                reason = "has less items in schema than in data"
                raise SchemaError(data, tree, reason=reason)
        if hasattr(schema, '__validator_leaf__'):
            return

        if len(data) != len(schema):
            raise SchemaError(data, tree, reason='length did not match schema')
Ejemplo n.º 11
0
 def data_sanity(self, data, tree=None):
     if not isinstance(data, list):
         name = self.name or 'IterableValidator'
         reason = 'expected a list but got %s' % safe_repr(data)
         msg = 'did not pass validation against callable: %s' % name
         raise Invalid('', tree or [], msg=msg, reason=reason, pair='value')
Ejemplo n.º 12
0
    def traverser(self, data, schema, tree):
        """
        Traverses the dictionary, recursing onto itself if
        it sees appropriate key/value pairs that indicate that
        there is a need for more validation in a branch below us.
        """
        if hasattr(schema, '__validator_leaf__'):
            return schema(data, tree)

        if hasattr(schema, 'must_validate'):  # cherry picking?
            if not len(schema.must_validate):
                reason = "must_validate attribute must not be empty"
                raise SchemaError(data, tree, reason=reason)
            data = sift(data, schema.must_validate)

        schema = self.sanitize_optionals(data, schema, tree)

        validated_indexes = []
        skip_missing_indexes = getattr(schema, 'must_validate', False)

        if len(data) < len(schema):
            # we have missing required items in data, but we don't know
            # which ones so find what may fail:
            data_keys = [v[1] for v in data.values()]
            schema_keys = [v[1] for v in schema.values()]

            def enforce_once(data_keys, schema_key):
                # XXX Go through all the data keys and try and see if they pass
                # validation against the schema. At this point it is impossible
                # to know which data key corresponds to what schema key
                # (because schema keys can be a function/callable) so it is
                # a *very* naive way to try and detect which one might be
                # missing
                for data_key in data_keys:
                    failed = None
                    try:
                        enforce(data_key, schema_key, tree, pair='key')
                        return
                    except Invalid:
                        failed = data_key, schema_key

                    if failed:
                        return failed

            for schema_key in schema_keys:
                failure = enforce_once(data_keys, schema_key)
                if failure:
                    _, failed_schema_key = failure
                    msg = "required key in data is missing: %s" % str(
                        failed_schema_key)
                    raise Invalid(None, tree, reason=msg, pair='key')

        for index in range(len(data)):
            self.length_equality(data, schema, index, tree)
            key, value = data[index]
            skey, svalue = schema[index]
            tree.append(key)

            # Validate the key before anything, to prevent recursing
            self.key_leaf(data[index], schema[index], tree)

            # If a dict is a value we need to recurse.
            # XXX Should we check isinstance(value, ndict) ?
            if isinstance(value, dict) and len(value):
                self.traverser(value, svalue, tree)
            else:
                self.value_leaf(data[index], schema[index], tree)
            if tree:
                tree.pop()

            validated_indexes.append(index)

        # XXX There is a chance we might have missing items from
        # the incoming data that are labeled as required from the schema
        # we should make sure *here* that we account for that and raise
        # the appropriate exception. Since the loop finished and everything
        # seems to have passed, this lack of check will give false positives.
        missing_indexes = set(schema.keys()).difference(validated_indexes)
        if missing_indexes:
            if skip_missing_indexes:
                return
            for i in missing_indexes:
                if not hasattr(schema[i], 'is_optional'):
                    required_key = schema[i][0]
                    tree.append('item[%s]' % i)
                    msg = "required item in schema is missing: %s" % str(
                        required_key)
                    raise Invalid(required_key, tree, reason=msg, pair='key')
Ejemplo n.º 13
0
 def validate(self):
     if self.data == {} and self.schema:
         msg = 'has no data to validate against schema'
         reason = 'an empty dictionary object was provided'
         raise Invalid(None, {}, msg=msg, reason=reason, pair='value')
     self.traverser(self.data, self.schema, [])