def errors(self, value):
     if value['foo'] != value['baz']:
         return [
             Error('Value foo does not match value baz',
                   pointer='foo')
         ]
     return []
Esempio n. 2
0
 def errors(self, value):  # type: (AnyType) -> ListType[Error]
     try:
         hash(value)
     except TypeError:
         return [
             Error('Value is not hashable'),
         ]
     return []
 def errors(self, value):
     errors = []
     for i, v in enumerate(value):
         if v > 500:
             errors.append(
                 Error('Whoop custom error',
                       pointer='{}'.format(i)))
     return errors
Esempio n. 4
0
 def errors(self, value):  # type: (AnyType) -> ListType[Error]
     if not isinstance(value, self.valid_type):
         return [
             Error('Not an instance of {}'.format(
                 getattr(self.valid_type, '__name__',
                         repr(self.valid_type))))
         ]
     return []
 def errors(self, value):
     errors = []
     for v in value:
         if v > 500:
             errors.append(
                 Error('Whoop custom error',
                       pointer='{}'.format(v)))
     return errors
 def test_decimal(self):  # type: () -> None
     """
     Tests decimal.Decimal object validation
     """
     self.assertEqual([], Decimal().errors(decimal.Decimal('1')))
     self.assertEqual([], Decimal().errors(decimal.Decimal('1.4')))
     self.assertEqual([], Decimal().errors(decimal.Decimal('-3.14159')))
     self.assertEqual([Error('Not a decimal')],
                      Decimal().errors('-3.14159'))
     self.assertEqual([Error('Not a decimal')], Decimal().errors(-3.14159))
     self.assertEqual([Error('Not a decimal')], Decimal().errors(15))
     self.assertEqual(
         [Error('Value not > 6')],
         Decimal(lt=12, gt=6).errors(decimal.Decimal('6')),
     )
     self.assertEqual(
         [Error('Value not < 12')],
         Decimal(lt=12, gt=6).errors(decimal.Decimal('12')),
     )
     self.assertEqual([],
                      Decimal(lt=12, gt=6).errors(decimal.Decimal('6.1')))
     self.assertEqual([],
                      Decimal(lt=12, gt=6).errors(decimal.Decimal('11.9')))
     self.assertEqual(
         [Error('Value not >= 6')],
         Decimal(lte=12, gte=6).errors(decimal.Decimal('5.9')),
     )
     self.assertEqual(
         [Error('Value not <= 12')],
         Decimal(lte=12, gte=6).errors(decimal.Decimal('12.1')),
     )
     self.assertEqual([],
                      Decimal(lte=12, gte=6).errors(decimal.Decimal('6')))
     self.assertEqual([],
                      Decimal(lte=12, gte=6).errors(decimal.Decimal('12')))
 def test_ipv4address(self):  # type: () -> None
     schema = IPv4Address()
     self.assertEqual(
         schema.errors('127.0.0.1'),
         [],
     )
     self.assertEqual(
         schema.errors('127.300.0.1'),
         [Error('Not a valid IPv4 address')],
     )
     self.assertEqual(
         schema.errors('127.0.0'),
         [Error('Not a valid IPv4 address')],
     )
     self.assertEqual(
         schema.errors('a2.12.55.3'),
         [Error('Not a valid IPv4 address')],
     )
 def test_latitude(self):  # type: () -> None
     schema = Latitude()
     self.assertEqual(
         schema.errors(89) or [],
         [],
     )
     self.assertEqual(
         schema.errors(-1.3412) or [],
         [],
     )
     self.assertEqual(
         schema.errors(180),
         [Error('Value not <= 90')],
     )
     self.assertEqual(
         schema.errors(-91),
         [Error('Value not >= -90')],
     )
    def test_hashable(self):  # type: () -> None
        assert Hashable('Another description 2').introspect() == {
            'type': 'hashable',
            'description': 'Another description 2',
        }

        assert Hashable().errors('this is hashable') == []
        assert Hashable().errors({'this', 'is', 'not', 'hashable'
                                  }) == [Error('Value is not hashable')]
 def test_limited_longitude(self):  # type: () -> None
     schema = Longitude(lte=-50)
     self.assertEqual(
         schema.errors(-51.2) or [],
         [],
     )
     self.assertEqual(
         schema.errors(-49.32) or [],
         [Error('Value not <= -50')],
     )
Esempio n. 11
0
 def errors(self, value):  # type: (AnyType) -> ListType[Error]
     # Get any basic type errors
     result = super(IPv4Address, self).errors(value)
     if result:
         return result
     # Check for IPv4-ness
     if ipv4_regex.match(value):
         return []
     else:
         return [Error('Not a valid IPv4 address')]
Esempio n. 12
0
    def errors(self, value):  # type: (AnyType) -> ListType[Error]
        if not isinstance(value, Mapping):
            return [Error('Not a mapping (dictionary)')]

        # check for extra keys (object is allowed in case this gets validated twice)
        extra_keys = [
            k for k in six.iterkeys(value)
            if k not in ('path', 'kwargs', 'object')
        ]
        if extra_keys:
            return [
                Error(
                    'Extra keys present: {}'.format(', '.join(
                        six.text_type(k) for k in sorted(extra_keys))),
                    code=ERROR_CODE_UNKNOWN,
                )
            ]

        sentinel = object()
        path = value.get('path', sentinel)
        if path is sentinel and not self.default_path:
            return [
                Error('Missing key (and no default specified): path',
                      code=ERROR_CODE_MISSING,
                      pointer='path')
            ]

        if not path or path is sentinel:
            path = self.default_path

        errors = self._populate_schema_cache_if_necessary(path)
        if errors:
            return [update_pointer(e, 'path') for e in errors]

        if isinstance(value, MutableMapping):
            value['path'] = path  # in case it was defaulted
            if self.add_class_object_to_dict:
                value['object'] = PythonPath.resolve_python_path(path)

        return [
            update_pointer(e, 'kwargs')
            for e in self._schema_cache[path].errors(value.get('kwargs', {}))
        ]
Esempio n. 13
0
    def errors(self, value):  # type: (AnyType) -> ListType[Error]
        try:
            is_valid = value in self.values
        except TypeError:
            # Unhashable values can't be used for membership checks.
            is_valid = False

        if not is_valid:
            return [Error(self._error_message, code=ERROR_CODE_UNKNOWN)]
        return []
Esempio n. 14
0
    def errors(self, value):  # type: (AnyType) -> ListType[Error]
        if not isinstance(value, self.valid_types):
            return [Error(self.type_error)]

        result = []
        if self.max_length is not None and len(value) > self.max_length:
            result.append(
                Error('List is longer than {}'.format(self.max_length)), )
        elif self.min_length is not None and len(value) < self.min_length:
            result.append(
                Error('List is shorter than {}'.format(self.min_length)), )
        for lazy_pointer, element in self._enumerate(value):
            result.extend(
                update_pointer(error, lazy_pointer.get())
                for error in (self.contents.errors(element) or []))

        if not result and self.additional_validator:
            return self.additional_validator.errors(value)

        return result
Esempio n. 15
0
    def errors(self, value):  # type: (AnyType) -> ListType[Error]
        if not isinstance(value, tuple):
            return [Error('Not a tuple')]

        result = []
        if len(value) != len(self.contents):
            result.append(
                Error(
                    'Number of elements {} does not match expected {}'.format(
                        len(value), len(self.contents))))

        for i, (c_elem, v_elem) in enumerate(zip(self.contents, value)):
            result.extend(
                update_pointer(error, i)
                for error in (c_elem.errors(v_elem) or []))

        if not result and self.additional_validator:
            return self.additional_validator.errors(value)

        return result
Esempio n. 16
0
    def test_schemaless(self):  # type: () -> None
        schema = ClassConfigurationSchema(base_class=BaseSomething)

        config = {
            'path': 'tests.test_fields_meta:SomethingWithJustKwargs'
        }  # type: dict
        value = schema.instantiate_from(config)
        assert config['object'] == SomethingWithJustKwargs
        assert isinstance(value, SomethingWithJustKwargs)
        assert value.kwargs == {}

        config = {
            'path': 'tests.test_fields_meta:SomethingWithJustKwargs',
            'kwargs': {
                'dog': 'Bree',
                'cute': True,
                'cat': b'Pumpkin'
            },
        }
        value = schema.instantiate_from(config)
        assert config['object'] == SomethingWithJustKwargs
        assert isinstance(value, SomethingWithJustKwargs)
        assert value.kwargs == {'dog': 'Bree', 'cute': True, 'cat': b'Pumpkin'}

        config = {
            'path': 'tests.test_fields_meta:SomethingWithJustKwargs',
            'kwargs': {
                b'Not unicode': False
            }
        }
        with pytest.raises(ValidationError) as error_context:
            schema.instantiate_from(config)
        assert error_context.value.args[0] == [
            Error('Not a unicode string',
                  code='INVALID',
                  pointer='kwargs.Not unicode')
            if six.PY2 else Error('Not a unicode string',
                                  code='INVALID',
                                  pointer='kwargs.{!r}'.format(b'Not unicode'))
        ]
        assert config['object'] == SomethingWithJustKwargs
Esempio n. 17
0
    def errors(self, value):  # type: (AnyType) -> ListType[Error]
        switch_value, valid = self._get_switch_value(value)
        if not valid:
            return [
                Error("Invalid switch value '{}'".format(switch_value),
                      code=ERROR_CODE_UNKNOWN)
            ]

        # Get field
        field = self.contents_map[switch_value]
        # Run field errors
        return field.errors(value)
Esempio n. 18
0
    def errors(self, value):  # type: (AnyType) -> ListType[Error]
        if not isinstance(value, six.text_type):
            return [Error('Not a unicode string')]

        try:
            thing = self.resolve_python_path(value)
        except ValueError:
            return [
                Error('Value "{}" is not a valid Python import path'.format(
                    value))
            ]
        except ImportError as e:
            return [Error('ImportError: {}'.format(six.text_type(e.args[0])))]
        except AttributeError as e:
            return [
                Error('AttributeError: {}'.format(six.text_type(e.args[0])))
            ]

        if self.value_schema:
            return self.value_schema.errors(thing)

        return []
Esempio n. 19
0
def _get_errors_for_currency_amount(
    currency_code,  # type: six.text_type
    value,  # type: int
    valid_currencies,  # type: AbstractSet[six.text_type]
    gt,  # type: Optional[int]
    gte,  # type: Optional[int]
    lt,  # type: Optional[int]
    lte,  # type: Optional[int]
):
    errors = []

    if currency_code not in valid_currencies:
        errors.append(Error('Not a valid currency code', code=ERROR_CODE_INVALID))
    if gt is not None and value <= gt:
        errors.append(Error('Value not > {}'.format(gt), code=ERROR_CODE_INVALID))
    if lt is not None and value >= lt:
        errors.append(Error('Value not < {}'.format(lt), code=ERROR_CODE_INVALID))
    if gte is not None and value < gte:
        errors.append(Error('Value not >= {}'.format(gte), code=ERROR_CODE_INVALID))
    if lte is not None and value > lte:
        errors.append(Error('Value not <= {}'.format(lte), code=ERROR_CODE_INVALID))

    return errors
Esempio n. 20
0
    def errors(self, value):  # type: (AnyType) -> ListType[Error]
        if not isinstance(value, six.text_type):
            return [Error('Not a unicode string currency amount')]

        parts = self._format.split(value)
        if len(parts) != 2:
            return [Error('Currency string does not match format CUR,1234 or CUR:1234')]

        currency = parts[0]
        try:
            value = int(parts[1])
        except ValueError:
            return [Error('Currency amount {} cannot be converted to an integer'.format(parts[1]))]

        return _get_errors_for_currency_amount(
            currency,
            value,
            self.valid_currencies,
            self.gt,
            self.gte,
            self.lt,
            self.lte,
        )
Esempio n. 21
0
    def test_schemaless_dict_empty(self):  # type: () -> None
        """
        Tests the schemaless dict without any schema at all
        (so the default Hashable: Anything)
        """
        schema = SchemalessDictionary()

        self.assertEqual(schema.errors({'key': 'value'}), [])

        self.assertEqual(schema.errors('a thing'), [Error('Not a dict')])

        self.assertEqual(schema.introspect(), {
            'type': 'schemaless_dictionary',
        })
Esempio n. 22
0
    def test_set(self):  # type: () -> None
        with pytest.raises(TypeError):
            # noinspection PyTypeChecker
            Set(UnicodeString(),
                additional_validator='Not a validator')  # type: ignore

        field = Set(UnicodeString())

        assert field.errors(
            ('hello', 'goodbye')) == [Error(message='Not a set or frozenset')]
        assert field.errors({'hello': 'goodbye'
                             }) == [Error(message='Not a set or frozenset')]
        assert field.errors(['hello', 'goodbye'
                             ]) == [Error(message='Not a set or frozenset')]
        assert field.errors(
            {'hello',
             2}) == [Error(message='Not a unicode string', pointer='[2]')]
        assert field.errors(frozenset(
            ('hello',
             2))) == [Error(message='Not a unicode string', pointer='[2]')]
        assert field.errors({'hello', 'goodbye'}) == []
        assert field.errors(frozenset(('hello', 'goodbye'))) == []

        class V(AdditionalCollectionValidator[AbstractSet]):
            def errors(self, value):
                errors = []
                for v in value:
                    if v > 500:
                        errors.append(
                            Error('Whoop custom error',
                                  pointer='{}'.format(v)))
                return errors

        field = Set(Integer(), additional_validator=V())

        assert field.errors({501, 'Not a number'}) == [
            Error(message='Not an integer', pointer='[Not a number]')
        ]
        assert field.errors(
            {501,
             499}) == [Error(message='Whoop custom error', pointer='501')]
        assert field.errors(frozenset(
            (501,
             499))) == [Error(message='Whoop custom error', pointer='501')]
        assert field.errors({500, 499}) == []
        assert field.errors(frozenset((500, 499))) == []
Esempio n. 23
0
    def test_multi_constant(self):  # type: () -> None
        """
        Tests constants with multiple options
        """
        schema = Constant(42, 36, 81, 9231)
        self.assertEqual(
            schema.errors(9231),
            [],
        )
        self.assertEqual(
            schema.errors(81),
            [],
        )
        self.assertEqual(
            schema.errors(360000),
            [
                Error('Value is not one of: 36, 42, 81, 9231',
                      code=ERROR_CODE_UNKNOWN)
            ],
        )
        self.assertEqual(
            schema.errors([42]),
            [
                Error('Value is not one of: 36, 42, 81, 9231',
                      code=ERROR_CODE_UNKNOWN)
            ],
        )

        with pytest.raises(TypeError):
            Constant(42, 36, 81, 9231, description='foo', unsupported='bar')

        with pytest.raises(ValueError):
            Constant()

        with pytest.raises(TypeError):
            Constant(42, 36, 81, 9231, description=b'not unicode')
Esempio n. 24
0
    def errors(self, value):  # type: (AnyType) -> ListType[Error]
        if not isinstance(value, currint.Amount):
            return [Error(
                'Not a currint.Amount instance',
                code=ERROR_CODE_INVALID,
            )]

        return _get_errors_for_currency_amount(
            value.currency.code,
            value.value,
            self.valid_currencies,
            self.gt,
            self.gte,
            self.lt,
            self.lte,
        )
Esempio n. 25
0
    def instantiate_from(
        self, configuration
    ):  # type: (MutableMapping[HashableType, AnyType]) -> AnyType
        if not isinstance(configuration, MutableMapping):
            raise ValidationError(
                [Error('Not a mutable mapping (dictionary)')])

        errors = self.errors(configuration)
        if errors:
            raise ValidationError(errors)

        clazz = configuration.get('object')
        if not clazz:
            clazz = PythonPath.resolve_python_path(configuration['path'])

        return clazz(**configuration.get('kwargs', {}))
Esempio n. 26
0
    def test_type_path(self):  # type: () -> None
        schema = TypePath(description='This is another test')
        assert schema.errors(b'Nope nope nope') == [
            Error('Not a unicode string')
        ]
        assert schema.errors('Nope nope nope') == [
            Error('Value "Nope nope nope" is not a valid Python import path')
        ]
        assert schema.errors('foo.bar:Hello') == [
            Error('ImportError: No module named foo.bar' if six.
                  PY2 else "ImportError: No module named 'foo'")
        ]
        assert schema.errors('conformity.fields:NotARealField') == [
            Error(
                "AttributeError: 'module' object has no attribute 'NotARealField'"
                if six.PY2 else
                "AttributeError: module 'conformity.fields' has no attribute 'NotARealField'"
            )
        ]
        assert schema.errors('conformity.fields:UnicodeString') == []
        assert schema.errors('conformity.fields.UnicodeString') == []
        assert schema.errors('conformity.fields.ByteString') == []
        assert schema.errors('conformity.fields:ByteString') == []
        assert schema.errors('tests.test_fields_meta.Foo') == []
        assert schema.errors('tests.test_fields_meta.Bar') == []
        assert schema.errors('tests.test_fields_meta.Baz') == []
        assert schema.errors('tests.test_fields_meta.Qux') == []
        assert schema.errors('tests.test_fields_meta:Qux.InnerQux') == []

        schema = TypePath(base_classes=Foo)
        assert schema.errors('tests.test_fields_meta.Foo') == []
        assert schema.errors('tests.test_fields_meta.Bar') == []
        assert schema.errors('tests.test_fields_meta.Baz') == [
            Error('Type {} is not one of or a subclass of one of: {}'.format(
                Baz, Foo)),
        ]
        assert schema.errors('conformity.fields.UnicodeString') == [
            Error('Type {} is not one of or a subclass of one of: {}'.format(
                TypePath.resolve_python_path(
                    'conformity.fields.UnicodeString'),
                Foo,
            )),
        ]
        assert schema.introspect() == {
            'type': 'python_path',
            'value_schema': {
                'type': 'type_reference',
                'base_classes': [six.text_type(Foo)],
            }
        }

        assert TypePath.resolve_python_path(
            'tests.test_fields_meta.Qux') == Qux
        assert TypePath.resolve_python_path(
            'tests.test_fields_meta:Qux.InnerQux') == Qux.InnerQux
Esempio n. 27
0
    def test_integers(self):  # type: () -> None
        schema = Integer(gt=0, lt=10)
        self.assertEqual([], schema.errors(1))
        self.assertEqual([Error('Not an integer')], schema.errors('one'))
        self.assertEqual([Error('Not an integer')], schema.errors(True))
        self.assertEqual([Error('Value not > 0')], schema.errors(0))
        self.assertEqual([Error('Value not < 10')], schema.errors(10))

        schema = Integer(gte=0, lte=10)
        self.assertEqual([Error('Value not >= 0')], schema.errors(-1))
        self.assertEqual([Error('Value not <= 10')], schema.errors(11))
Esempio n. 28
0
    def test_sequence(self):  # type: () -> None
        with pytest.raises(TypeError):
            # noinspection PyTypeChecker
            Sequence(UnicodeString(),
                     additional_validator='Not a validator')  # type: ignore

        field = Sequence(UnicodeString())

        assert field.errors({'hello':
                             'goodbye'}) == [Error(message='Not a sequence')]
        assert field.errors({'hello',
                             'goodbye'}) == [Error(message='Not a sequence')]
        assert field.errors(
            ['hello',
             2]) == [Error(message='Not a unicode string', pointer='1')]
        assert field.errors(
            (1,
             'world')) == [Error(message='Not a unicode string', pointer='0')]
        assert field.errors(['hello', 'goodbye']) == []
        assert field.errors(('hello', 'goodbye')) == []

        class V(AdditionalCollectionValidator[SequenceType]):
            def errors(self, value):
                errors = []
                for i, v in enumerate(value):
                    if v > 500:
                        errors.append(
                            Error('Whoop another error',
                                  pointer='{}'.format(i)))
                return errors

        field = Sequence(Integer(), additional_validator=V())

        assert field.errors([501, 'Not a number dude']) == [
            Error(message='Not an integer', pointer='1')
        ]
        assert field.errors(
            [501, 499]) == [Error(message='Whoop another error', pointer='0')]
        assert field.errors(
            (501, 499)) == [Error(message='Whoop another error', pointer='0')]
        assert field.errors([500, 499]) == []
        assert field.errors((500, 499)) == []
Esempio n. 29
0
    def test_object_instance(self):  # type: () -> None
        class Thing(object):
            pass

        class Thingy(Thing):
            pass

        class SomethingElse(object):
            pass

        schema = ObjectInstance(Thing, description='Yessiree')

        self.assertEqual(schema.errors(Thing()), [])

        # subclasses are valid
        self.assertEqual(schema.errors(Thingy()), [])

        self.assertEqual(schema.errors(SomethingElse()),
                         [Error('Not an instance of Thing')])

        assert schema.introspect() == {
            'type': 'object_instance',
            'description': 'Yessiree',
            'valid_type': repr(Thing),
        }

        schema = ObjectInstance((Thing, SomethingElse))
        assert schema.errors(Thing()) == []
        assert schema.errors(Thingy()) == []
        assert schema.errors(SomethingElse()) == []

        with pytest.raises(TypeError):
            # noinspection PyTypeChecker
            ObjectInstance('not a type')  # type: ignore

        with pytest.raises(TypeError):
            # noinspection PyTypeChecker
            ObjectInstance(
                (Thing, SomethingElse, 'also not a type'))  # type: ignore
 def test_ipv6address(self):  # type: () -> None
     schema = IPv6Address()
     self.assertEqual(
         schema.errors('::2'),
         [],
     )
     self.assertEqual(
         schema.errors('abdf::4'),
         [],
     )
     self.assertEqual(
         schema.errors('34de:e23d::233e:32'),
         [],
     )
     self.assertEqual(
         schema.errors('::ffff:222.1.41.90'),
         [],
     )
     self.assertEqual(
         schema.errors('1232:d4af:6023:1afc:cfed:0239d:0934:0923d'),
         [],
     )
     self.assertEqual(
         schema.errors('1232:d4af:6023:1afc:cfed:0239d:0934:0923d:3421'),
         [Error('Not a valid IPv6 address (too many colons)')],
     )
     self.assertEqual(
         schema.errors('1:::42'),
         [Error('Not a valid IPv6 address (shortener not bounded)')],
     )
     self.assertEqual(
         schema.errors('1351:z::3'),
         [Error('Not a valid IPv6 address (invalid hextet)')],
     )
     self.assertEqual(
         schema.errors('dead:beef::3422:23::1'),
         [Error('Not a valid IPv6 address (multiple shorteners)')],
     )
     self.assertEqual(
         schema.errors('dead:beef::127.0.0.1:0'),
         [Error('Not a valid IPv6 address (v4 section not at end)')],
     )
     self.assertEqual(
         schema.errors('dead:beef::127.0.0.300'),
         [Error('Not a valid IPv6 address (v4 section not valid address)')],
     )