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
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(