Esempio n. 1
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')
Esempio n. 2
0
    def enforce(self, data, schema, 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, pair='value')
            except SchemaError: # FIXME this is utterly redundant, and also happens in RecursiveValidator
                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:
                    assert data[item_index] == schema
            except AssertionError:
                tree.append('list[%s]' % item_index)
                raise Invalid(schema, tree, pair='item')
Esempio n. 3
0
def string(_object):
    """
    Validates a given input is of type string.

    Example usage::

        data = {'a' : 21}
        schema = (string, 21)

    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.
    """
    if is_callable(_object):
        _validator = _object

        @wraps(_validator)
        def decorated(value):
            ensure(isinstance(value, basestring), "not of type string")
            return _validator(value)
        return decorated
    ensure(isinstance(_object, basestring), "not of type string")
Esempio n. 4
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
Esempio n. 5
0
 def _format_message(self):
     if self._msg:
         return self._msg
     if is_callable(self.schema_item):
         msg = "did not pass validation against callable: %s" % (self.schema_item.__name__)
     else:
         msg = "did not match %s" % (repr(self.schema_item))
     return msg
Esempio n. 6
0
 def _format_message(self):
     reason = self._formatted_reason()
     if self._msg:
         return self._msg
     if is_callable(self.schema_item):
         msg = "did not pass validation against callable: %s"\
               "%s" % (self.schema_item.__name__, reason)
     else:
         msg = "did not match %s%s" % (repr(self.schema_item), reason)
     return msg
Esempio n. 7
0
def enforce(data_item, schema_item, tree, pair):
    if is_callable(schema_item):
        try:
            schema_item(data_item)
        except AssertionError:
            e = sys.exc_info()[1]
            raise Invalid(schema_item, tree, reason=e, pair=pair)
    else:
        try:
            assert data_item == schema_item
        except AssertionError:
            if pair == 'value':
                tree.append(data_item)
            raise Invalid(schema_item, tree, pair=pair)
Esempio n. 8
0
    def __call__(self, data, tree):
        index = len(data) - 1
        validator = IterableValidator(data, self.schema, [], index=index)
        for item_index in range(len(data)):
            try:
                return validator.leaf(item_index)
            except Invalid:
                pass

        tree.append('list[]')
        if is_callable(self.schema):
            msg = "did not contain any valid items against callable: %s" % self.schema.__name__
        else:
            msg = "did not contain any valid items matching %s" % repr(self.schema)
        raise Invalid(self.schema, tree, pair='value', msg=msg)
Esempio n. 9
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')
Esempio n. 10
0
def not_empty(_object):
    """
    Validates the given input (has to be a valid data structure) is empty.
    Input *has* to be one of: `list`, `dict`, or `string`.

    It is specially useful when most of the validators being created are
    dealing with data structures that should not be empty.
    """
    if is_callable(_object):
        _validator = _object

        @wraps(_validator)
        @instance_of()
        def decorated(value):
            ensure(value, "%s is empty" % safe_repr(value))
            return _validator(value)
        return decorated
    try:
        ensure(len(_object), "%s is empty" % safe_repr(_object))
    except TypeError:
        raise AssertionError("not of any valid types: [list, dict, str]")
Esempio n. 11
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)
Esempio n. 12
0
def optional(_object):
    """
    This decorator has a double functionality, it can wrap validators and make
    them optional or it can wrap keys and make that entry optional.

    **Optional Validator:**
    Allows to have validators work only when there is a value that contains
    some data, otherwise it will just not pass the information to the actual
    validator and will not fail as a result.

    As any normal decorator, it can be used corectly with the decorator
    syntax or in the actual schema.

    This is how it would look in a schema::

        ('key', optional(my_validator))

    Where ``my_validator`` can be any validator that accepts a single
    argument.

    In case a class based validator is being used (like the ``recursive`` or
    ``iterables`` then it would look like::

        ('key', optional(class_validator(('key', 'value'))))

    Of course, the schema should vary depending on your needs, it is just the
    way of constructing the validator call that should be important.

    **Optional Keys:**
    Sometimes a given data structure may present optional entries. For example
    this data::

        data = {'required': 1, 'optional': 2}

    To represent this, you will need to declare the `optional` key in the
    schema but by wrapping the key with this decorator you will basically tell
    the validation engine that if that key is present it should be validated,
    otherwise, it should be skipped. This is how the schema would look::

        schema = (('required', 1), (optional('optional'), 1))

    The above schema would allow data that is missing the ``optional`` key. The
    data below would pass validation without any issues::

        data = {'required': 1}
    """
    if is_callable(_object):
        validator = _object

        @wraps(validator)
        def decorated(value):
            if value:
                return validator(value)
            return

        return decorated
    else:
        def optional(*args):
            return _object
        optional.is_optional = True
        optional._object = _object
        return optional
Esempio n. 13
0
 def test_is_callable(self):
     result = utils.is_callable(self.fake_callable)
     assert result is True
Esempio n. 14
0
 def test_is_not_callable(self):
     result = utils.is_callable(1)
     assert result is False
Esempio n. 15
0
 def __init__(self, *args):
     for i in args:
         if not is_callable(i):
             raise TypeError("got a non-callable argument: %s" % repr(i))
     self.args = args
Esempio n. 16
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)
        self.is_alpha_ordered(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[0] for v in data.values()]
            schema_keys = [v[0] 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

            # if there are no callables in the schema keys, just
            # find the missing data key directly
            if all([not is_callable(s) for s in schema_keys]):
                for schema_key in schema_keys:
                    if schema_key not in data_keys:
                        msg = "required key in data is missing: %s" % str(schema_key)
                        raise Invalid(None, tree, reason=msg, pair='key')

            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')