Exemple #1
0
    def test_nullable(self):
        constant = Constant('one', 'two')
        schema = Nullable(constant)
        self.assertEqual([], schema.errors(None))
        self.assertEqual([], schema.errors('one'))
        self.assertEqual([], schema.errors('two'))
        self.assertEqual(1, len(schema.errors('three')))
        self.assertEqual(
            {
                'type': 'nullable',
                'nullable': constant.introspect()
            }, schema.introspect())

        boolean = Boolean(description='This is a test description')
        schema = Nullable(boolean)
        self.assertEqual([], schema.errors(None))
        self.assertIsNone(schema.errors(True))
        self.assertIsNone(schema.errors(False))
        self.assertEqual(1, len(schema.errors('true')))
        self.assertEqual(1, len(schema.errors(1)))
        self.assertEqual({
            'type': 'nullable',
            'nullable': boolean.introspect()
        }, schema.introspect())

        string = UnicodeString()
        schema = Nullable(string)
        self.assertEqual([], schema.errors(None))
        self.assertIsNone(schema.errors('hello, world'))
        self.assertEqual(1, len(schema.errors(b'hello, world')))
        self.assertEqual({
            'type': 'nullable',
            'nullable': string.introspect()
        }, schema.introspect())
    def test_tuple(self):
        with pytest.raises(TypeError):
            Tuple(UnicodeString(),
                  Integer(),
                  Boolean(),
                  additional_validator='Not a validator')  # type: ignore

        field = Tuple(UnicodeString(), Integer(), Boolean())

        assert field.errors(['foo', 'bar',
                             'baz']) == [Error(message='Not a tuple')]
        assert field.errors({'foo': 'bar'}) == [Error(message='Not a tuple')]
        assert field.errors({'foo', 'bar',
                             'baz'}) == [Error(message='Not a tuple')]
        assert field.errors(
            ('foo', 'bar',
             True)) == [Error(message='Not an integer', pointer='1')]
        assert field.errors(('foo', 12, False)) == []
        assert field.errors(('foo', 12, True)) == []

        class V(AdditionalCollectionValidator[TupleType[AnyType]]):
            def errors(self, value):
                if value[2] is not True:
                    return [Error('The third value must be True', pointer='2')]
                return []

        field = Tuple(UnicodeString(),
                      Integer(),
                      Boolean(),
                      additional_validator=V())

        assert field.errors(('foo', 12, False)) == [
            Error(message='The third value must be True', pointer='2')
        ]
        assert field.errors(('foo', 12, True)) == []
    def test_provider_decorator(self):  # type: () -> None
        with pytest.raises(TypeError):
            # noinspection PyTypeChecker
            ClassConfigurationSchema.provider(Boolean())  # type: ignore

        schema = Dictionary({})

        decorator = ClassConfigurationSchema.provider(schema)

        class IsAClass(object):
            pass

        def is_not_a_class():
            pass

        with pytest.raises(TypeError):
            decorator(is_not_a_class)  # type: ignore

        cls = decorator(IsAClass)
        assert cls is IsAClass
        assert getattr(cls, '_conformity_initialization_schema') is schema

        another_schema = Dictionary({})

        @ClassConfigurationSchema.provider(another_schema)
        class Sample(object):
            pass

        assert getattr(Sample,
                       '_conformity_initialization_schema') is another_schema
Exemple #4
0
    List,
    SchemalessDictionary,
    UnicodeString,
)

ActionRequestSchema = Dictionary(
    {
        'action': UnicodeString(),
        'body': SchemalessDictionary(key_type=UnicodeString()),
    },
    optional_keys=['body'],
)

ControlHeaderSchema = Dictionary(
    {
        'continue_on_error': Boolean(),
    },
    allow_extra_keys=True,
)

ContextHeaderSchema = Dictionary(
    {
        'switches': List(Integer()),
        'correlation_id': UnicodeString(),
    },
    allow_extra_keys=True,
)

JobRequestSchema = Dictionary(
    {
        'control': ControlHeaderSchema,
    def test_dictionary_ordering(self):  # type: () -> None
        schema1 = Dictionary(
            OrderedDict((
                ('foo', UnicodeString()),
                ('bar', Boolean()),
                ('baz', List(Integer())),
            )),
            optional_keys=('foo', ),
            description='Hello, world',
        )

        assert schema1.introspect()['contents'] == {
            'baz': List(Integer()).introspect(),
            'foo': UnicodeString().introspect(),
            'bar': Boolean().introspect(),
        }

        assert schema1.introspect()['display_order'] == ['foo', 'bar', 'baz']

        schema2 = schema1.extend(
            OrderedDict((
                ('bar', Integer()),
                ('qux', Set(UnicodeString())),
                ('moon', Tuple(Decimal(), UnicodeString())),
            )))

        assert schema2.introspect()['contents'] == {
            'baz': List(Integer()).introspect(),
            'foo': UnicodeString().introspect(),
            'moon': Tuple(Decimal(), UnicodeString()).introspect(),
            'bar': Integer().introspect(),
            'qux': Set(UnicodeString()).introspect(),
        }

        assert schema2.introspect()['display_order'] == [
            'foo', 'bar', 'baz', 'qux', 'moon'
        ]

        assert not schema1.errors({'bar': True, 'foo': 'Hello', 'baz': [15]})

        errors = schema1.errors({
            'baz': 'Nope',
            'foo': False,
            'bar': ['Heck nope']
        })

        assert errors == [
            Error(code='INVALID',
                  pointer='foo',
                  message='Not a unicode string'),
            Error(code='INVALID', pointer='bar', message='Not a boolean'),
            Error(code='INVALID', pointer='baz', message='Not a list'),
        ]

        assert not schema2.errors(
            {
                'bar': 91,
                'foo': 'Hello',
                'qux': {'Yes'},
                'baz': [15],
                'moon': (decimal.Decimal('15.25'), 'USD')
            }, )

        errors = schema2.errors({
            'baz': 'Nope',
            'foo': False,
            'bar': ['Heck nope'],
            'qux': 'Denied',
            'moon': 72
        })

        assert errors == [
            Error(code='INVALID',
                  pointer='foo',
                  message='Not a unicode string'),
            Error(code='INVALID', pointer='bar', message='Not an integer'),
            Error(code='INVALID', pointer='baz', message='Not a list'),
            Error(code='INVALID',
                  pointer='qux',
                  message='Not a set or frozenset'),
            Error(code='INVALID', pointer='moon', message='Not a tuple'),
        ]
    def test_dictionary_extension(self):  # type: () -> None
        schema1 = Dictionary(
            {
                'foo': UnicodeString(),
                'bar': Boolean(),
            },
            optional_keys=('foo', ),
            description='Hello, world',
        )

        schema2 = schema1.extend(
            {
                'bar': Integer(),
                'baz': List(Integer()),
            },
            optional_keys=('baz', ),
        )

        schema3 = schema1.extend(
            {
                'bar': Integer(),
                'baz': List(Integer()),
            },
            optional_keys=('baz', ),
            allow_extra_keys=True,
            description='Goodbye, universe',
            replace_optional_keys=True,
        )

        self.assertEqual(
            Dictionary(
                {
                    'foo': UnicodeString(),
                    'bar': Integer(),
                    'baz': List(Integer()),
                },
                optional_keys=(
                    'foo',
                    'baz',
                ),
                allow_extra_keys=False,
                description='Hello, world',
            ).introspect(),
            schema2.introspect(),
        )

        self.assertEqual(
            Dictionary(
                {
                    'foo': UnicodeString(),
                    'bar': Integer(),
                    'baz': List(Integer()),
                },
                optional_keys=('baz', ),
                allow_extra_keys=True,
                description='Goodbye, universe',
            ).introspect(),
            schema3.introspect(),
        )

        assert 'display_order' not in schema1.introspect()
        assert 'display_order' not in schema2.introspect()
        assert 'display_order' not in schema3.introspect()
class InvalidProvider(object):
    _conformity_initialization_schema = Boolean()
    def test_subclass_definition(self):  # type: () -> None
        class ImmutableDict(Mapping):
            def __init__(self, underlying):
                self.underlying = underlying

            def __contains__(self, item):
                return item in self.underlying

            def __getitem__(self, k):
                return self.underlying[k]

            def get(self, k, default=None):
                return self.underlying.get(k, default)

            def __iter__(self):
                return iter(self.underlying)

            def __len__(self):
                return len(self.underlying)

            def keys(self):
                return self.underlying.keys()

            def items(self):
                return self.underlying.items()

            def values(self):
                return self.underlying.values()

        class ExtendedSchema(ClassConfigurationSchema):
            base_class = BaseSomething
            default_path = 'tests.test_fields_meta.AnotherSomething'
            description = 'Neat-o schema thing'

        schema = ExtendedSchema()

        config = {}  # type: dict
        with pytest.raises(ValidationError) as error_context:
            schema.instantiate_from(config)
        assert error_context.value.args[0] == [
            Error('Missing key: baz', code='MISSING', pointer='kwargs.baz'),
        ]
        assert config['object'] == AnotherSomething

        config = {'kwargs': {'baz': None}}
        value = schema.instantiate_from(config)
        assert isinstance(value, AnotherSomething)
        assert value.baz is None
        assert value.qux == 'unset'
        assert config['object'] == AnotherSomething

        config2 = ImmutableDict({'kwargs': {'baz': None}})
        assert schema.errors(config2) == []
        assert 'object' not in config2

        assert schema.introspect() == {
            'type':
            'class_config_dictionary',
            'description':
            'Neat-o schema thing',
            'default_path':
            'tests.test_fields_meta.AnotherSomething',
            'base_class':
            'BaseSomething',
            'switch_field':
            'path',
            'switch_field_schema':
            TypePath(base_classes=BaseSomething).introspect(),
            'kwargs_field':
            'kwargs',
            'kwargs_contents_map': {
                'tests.test_fields_meta.AnotherSomething':
                Dictionary(
                    {
                        'baz': Nullable(UnicodeString()),
                        'qux': Boolean()
                    },
                    optional_keys=('qux', ),
                ).introspect(),
            },
        }
    def test_inline_definition_no_default_or_base_class(
            self):  # type: () -> None
        schema = ClassConfigurationSchema()

        assert schema.errors('Not a dict') == [
            Error('Not a mapping (dictionary)')
        ]
        assert schema.errors({
            'foo': 'bar',
            'baz': 'qux',
            'path': 'unprocessed',
            'kwargs': {},
            'object': Foo
        }) == [Error('Extra keys present: baz, foo', code='UNKNOWN')]
        assert schema.errors({}) == [
            Error('Missing key (and no default specified): path',
                  code='MISSING',
                  pointer='path'),
        ]
        assert schema.errors({'path': 'foo.bar:Hello'}) == [
            Error(
                'ImportError: No module named foo.bar'
                if six.PY2 else "ImportError: No module named 'foo'",
                pointer='path',
            )
        ]
        assert schema.errors({'path': 'tests.test_fields_meta.Foo'}) == [
            Error(
                "Neither class 'tests.test_fields_meta.Foo' nor one of its superclasses was decorated with "
                "@ClassConfigurationSchema.provider",
                pointer='path',
            )
        ]
        assert schema.errors({
            'path': 'tests.test_fields_meta:InvalidProvider'
        }) == [
            Error(
                "Class 'tests.test_fields_meta:InvalidProvider' attribute '_conformity_initialization_schema' should be a "
                "Dictionary or SchemalessDictionary Conformity field or one of their subclasses",
                pointer='path',
            )
        ]

        config = {
            'path': 'tests.test_fields_meta:BasicProvider'
        }  # type: Dict[HashableType, AnyType]
        assert sorted(schema.errors(config)) == [
            Error('Missing key: bar', code='MISSING', pointer='kwargs.bar'),
            Error('Missing key: foo', code='MISSING', pointer='kwargs.foo'),
        ]
        assert config['object'] == BasicProvider

        with pytest.raises(ValidationError) as error_context:
            # noinspection PyTypeChecker
            schema.instantiate_from('Not a dict')  # type: ignore
        assert error_context.value.args[0] == [
            Error('Not a mutable mapping (dictionary)')
        ]

        config = {'path': 'tests.test_fields_meta:BasicProvider'}
        with pytest.raises(ValidationError) as error_context:
            schema.instantiate_from(config)
        assert sorted(error_context.value.args[0]) == [
            Error('Missing key: bar', code='MISSING', pointer='kwargs.bar'),
            Error('Missing key: foo', code='MISSING', pointer='kwargs.foo'),
        ]
        assert config['object'] == BasicProvider

        config = {
            'path': 'tests.test_fields_meta:BasicProvider',
            'kwargs': {
                'foo': 'Fine',
                'bar': 'Bad'
            }
        }
        assert schema.errors(config) == [
            Error('Not a boolean', pointer='kwargs.bar')
        ]
        assert config['object'] == BasicProvider

        config = {
            'path': 'tests.test_fields_meta:BasicProvider',
            'kwargs': {
                'foo': 'Fine',
                'bar': 'Bad'
            }
        }
        with pytest.raises(ValidationError) as error_context:
            schema.instantiate_from(config)
        assert error_context.value.args[0] == [
            Error('Not a boolean', pointer='kwargs.bar')
        ]
        assert config['object'] == BasicProvider

        config = {
            'path': 'tests.test_fields_meta:BasicProvider',
            'kwargs': {
                'foo': 'Fine',
                'bar': True
            }
        }
        assert schema.errors(config) == []
        assert config['object'] == BasicProvider

        config = {
            'path': 'tests.test_fields_meta:BasicProvider',
            'kwargs': {
                'foo': 'Fine',
                'bar': True
            }
        }
        value = schema.instantiate_from(config)
        assert isinstance(value, BasicProvider)
        assert value.foo == 'Fine'
        assert value.bar is True
        assert config['object'] == BasicProvider

        schema = ClassConfigurationSchema()
        with pytest.raises(ValidationError):
            schema.initiate_cache_for('foo.bar:Hello')
        schema.initiate_cache_for('tests.test_fields_meta.BasicProvider')
        schema.initiate_cache_for('tests.test_fields_meta:BasicProvider')
        assert schema.introspect() == {
            'type': 'class_config_dictionary',
            'base_class': 'object',
            'switch_field': 'path',
            'switch_field_schema': TypePath(base_classes=object).introspect(),
            'kwargs_field': 'kwargs',
            'kwargs_contents_map': {
                'tests.test_fields_meta.BasicProvider':
                Dictionary({
                    'foo': UnicodeString(),
                    'bar': Boolean()
                }, ).introspect(),
                'tests.test_fields_meta:BasicProvider':
                Dictionary({
                    'foo': UnicodeString(),
                    'bar': Boolean()
                }, ).introspect(),
            },
        }

        schema = ClassConfigurationSchema(add_class_object_to_dict=False)
        config = {
            'path': 'tests.test_fields_meta:BasicProvider',
            'kwargs': {
                'foo': 'Fine',
                'bar': True
            }
        }
        value = schema.instantiate_from(config)
        assert isinstance(value, BasicProvider)
        assert value.foo == 'Fine'
        assert value.bar is True
        assert 'object' not in config
                  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


class InvalidProvider(object):
    _conformity_initialization_schema = Boolean()


@ClassConfigurationSchema.provider(
    Dictionary({
        'foo': UnicodeString(),
        'bar': Boolean()
    }))
class BasicProvider(object):
    def __init__(self, foo, bar):
        self.foo = foo
        self.bar = bar


class BaseSomething(object):
    pass


@ClassConfigurationSchema.provider(
    Dictionary({
        'foo': Boolean(),
        'bar': Constant('walk', 'run')
        UnicodeString(
            description='The name of the service action to execute.'),
        'body':
        SchemalessDictionary(
            key_type=UnicodeString(),
            description='The request parameters for this action.'),
    },
    optional_keys=('body', ),
)
"""The Conformity schema with which action requests are validated."""

ControlHeaderSchema = Dictionary(
    {
        'continue_on_error':
        Boolean(
            description=
            'Whether to continue executing more actions in a multi-action job request if an action '
            'results in an error.', ),
        'suppress_response':
        Boolean(
            description=
            'Whether to complete processing a request without sending a response back to the client '
            '(defaults to false).'),
    },
    allow_extra_keys=True,
    optional_keys=('suppress_response', ),
)

ContextHeaderSchema = Dictionary(
    {
        'caller':
        UnicodeString(