def test_validation_errors(self): validator = dschema.Validator({ 'app_auth': { 'id': dschema.prop(required=True), 'token': dschema.prop(required=True), dschema.Required: True }, 'integer': dschema.prop(required=True, type=int) }) data = {'app_auth': {'token': 'somerandomthing'}, 'integer': 1} with self.assertRaises(dschema.MissingKeyError): validator.validate(data) data = {'integer': 1} with self.assertRaises(dschema.MissingKeyError): validator.validate(data) data = { 'app_auth': { 'id': 12345, 'token': 'somerandomthing' }, 'integer': 'notinteger' } with self.assertRaises(dschema.TypeValidationError): validator.validate(data)
def test_schema_errors(self): schema = {'bad': dschema.prop(required=True, default='cant-have-both')} with self.assertRaises(dschema.SchemaError): dschema.Validator(schema).validate({'bad': 'stuff'}) schema = {'no_type_validator': dschema.prop(required=True, type='int')} with self.assertRaises(dschema.SchemaMissingTypeError): dschema.Validator(schema).validate({'no_type_validator': 1})
def test_prop(self): d = dschema.prop(default=2, dict=True, type=int, required=True) self.assertDictEqual(d, { '@default': 2, "@dict": True, "@type": int, "@required": True }) with self.assertRaises(ValueError): dschema.prop(unknown_arg='BAD')
def test_required(self): validator = dschema.Validator({ 'test1': { 'a': dschema.prop(required=True) }, 'test2': { 'test3': { dschema.Required: True, 'a': dschema.prop(default=1), 'b': dschema.prop(default=2) } }, 'test4': { 'test5': { dschema.Required: True, 'a': dschema.prop(required=True), 'b': dschema.prop(default=2) } } }) # test2 is optional, test3 must exist if test2 exist though # shouldn't throw validator.validate({ 'test1': { 'a': 1 }, }) with self.assertRaises(dschema.ValidationError): # test.a must exist validator.validate({ 'test1': 1, }) with self.assertRaises(dschema.ValidationError): # test2.test3 must exist validator.validate({'test1': {'a': 1}, 'test2': 1}) # test2 is optional, test3 must exist if test2 exist though # shouldn't throw validator.validate({'test1': {'a': 1}, 'test2': {'test3': {'a': 1}}}) with self.assertRaises(dschema.ValidationError): # test4.test5.a must exist validator.validate({'test1': {'a': 1}, 'test4': {'test5': None}}) # test4.test5.a exists, shouldn't throw validator.validate({'test1': {'a': 1}, 'test4': {'test5': {'a': 1}}})
def test_dict(self): validator = dschema.Validator({ 'dict': dschema.Dict, 'in_schema_not_dict': dschema.prop(default=dict()) }) # shouldn't throw result = validator.validate( { 'dict': { 'a': 'b' }, 'not_dict': { 'c': 'd' }, # unhandled values should be namespacified when return_namespace=True, 'in_schema_not_dict': { 'e': 'f' } }, namespace=True, extra_keys=True) self.assertDictEqual(result.dict, {'a': 'b'}) self.assertTrue(isinstance(result.not_dict, dschema.Namespace)) self.assertEqual(result.not_dict.c, 'd') self.assertTrue( isinstance(result.in_schema_not_dict, dschema.Namespace)) self.assertTrue(result.in_schema_not_dict.e, 'f')
def test_type_validation(self): def type_test(param): def type_test2(p): self.assertEqual(p, param) return p return type_test2 validator = dschema.Validator({ 'a': { 'b': dschema.prop(type=int) }, 'c': { 'd': 'int' }, 'e': 'test_validate', 'f': type_test('test2'), 'dict': dschema.Dict }) validator.add_type('int', int) validator.add_type('test_validate', type_test('test1')) result = validator.validate({ 'a': { 'b': 1 }, 'c': { 'd': 2 }, 'e': 'test1', 'f': 'test2', 'dict': { 'a': 'b' } }) self.assertEqual(result['a']['b'], 1) self.assertEqual(result['c']['d'], 2) self.assertEqual(result['e'], 'test1') self.assertEqual(result['f'], 'test2')
def test_extraneous_values(self): validator = dschema.Validator({ 'namespace': { 'nested': { 'a': dschema.prop(required=True), dschema.Required: True }, dschema.Required: True } }) with self.assertRaises(dschema.ExtraKeysError): validator.validate({ 'namespace': { 'nested': { 'a': 1, 'b': 2 # not allowed } } }) with self.assertRaises(dschema.ExtraKeysError): validator.validate({ 'namespace': { 'nested': { 'a': 1 }, 'b': 2 # not allowed } }) with self.assertRaises(dschema.ExtraKeysError): validator.validate({ 'namespace': { 'nested': { 'a': 1 } }, 'b': 2 # not allowed })
def test_fill_defaults_algorithm(self): # test the defaults filler algorithm v = dschema.Validator({}) schema = { 'l': { '@dict': True, 'm': dschema.prop(default={'test1': 1}, dict=True) }, 'a': { 'b': dschema.prop(default={'test2': 2}, dict=True) } } r = v._fill_schema_defaults(schema, '.', True) self.assertTrue(hasattr(r, 'l')) self.assertDictEqual(r.l, {'m': {'test1': 1}}) self.assertTrue(hasattr(r, 'a')) self.assertTrue(hasattr(r.a, 'b')) self.assertDictEqual(r.a.b, {'test2': 2}) r = v._fill_schema_defaults(schema, '.', False) self.assertDictEqual(r, { 'l': { 'm': { 'test1': 1 } }, 'a': { 'b': { 'test2': 2 } } }) r = v._fill_schema_defaults({dschema.Default: 1}, '.', False) self.assertEqual(r, 1) r = v._fill_schema_defaults({dschema.Default: {'test': 1}}, '.', True) self.assertEqual(type(r), dschema.Namespace) self.assertTrue(hasattr(r, 'test')) self.assertEqual(r.test, 1) r = v._fill_schema_defaults( { dschema.Dict: True, dschema.Default: { 'test': 1 } }, '.', True) self.assertEqual(type(r), dict) self.assertIn('test', r) self.assertEqual(r['test'], 1) r = v._fill_schema_defaults( { dschema.Dict: True, dschema.Default: { 'test': 1 } }, '.', False) self.assertEqual(type(r), dict) self.assertIn('test', r) self.assertEqual(r['test'], 1)
def test_application_of_schema_to_defaults(self): v = dschema.Validator( schema={ "node": { dschema.Default: { "node2": { "a": 1 } }, "node2": { "a": dschema.prop(required=True, type=lambda x: x + 1) } } }) r = v.validate({"node": None}, namespace=True) self.assertEqual(r.node.node2.a, 2) v.schema = { "node": { dschema.Default: { "a": 1 }, "a": dschema.prop(required=True, type=lambda x: x + 1) } } r = v.validate({}, namespace=True) self.assertEqual(r.node.a, 2) v.schema = { "node": { dschema.Default: { "node2": { "a": 1 } }, "node2": { "a": dschema.prop(required=True, type=lambda x: x + 1) } } } r = v.validate({}) self.assertEqual(r['node']['node2']['a'], 2) v.schema = { "node": { dschema.Default: { "a": 1 }, "a": dschema.prop(required=True, type=lambda x: x + 1) } } r = v.validate({"node": None}) self.assertEqual(r['node']['a'], 2) v.schema = {"a": dschema.prop(default='notint', type=int)} with self.assertRaises(dschema.SchemaDefaultError) as err: v.validate({}) self.assertTrue( isinstance(err.exception.validation_error, dschema.TypeValidationError)) v.schema = { "a": { dschema.Default: { "b": None }, "b": { dschema.Default: None, "c": dschema.prop(required=True, type=int) } } } with self.assertRaises(dschema.SchemaDefaultError) as err: v.validate({}) self.assertTrue( isinstance(err.exception.validation_error, dschema.MissingKeyError)) v.schema = { 'a': { 'b': { 'c': dschema.prop(default='d', type=lambda x: x + 'd') }, } } r = v.validate({}, namespace=True) self.assertEqual(r.a.b.c, 'dd') v.schema = { 'a': { 'b': { 'c': dschema.prop(default='d', type=lambda x: x + 'd') }, } } r = v.validate({}) self.assertEqual(r['a']['b']['c'], 'dd') v.schema = { 'a': { 'b': { 'c': dschema.prop(default='notint', type=int) }, } } with self.assertRaises(dschema.SchemaDefaultError) as err: v.validate({}, namespace=True) self.assertTrue( isinstance(err.exception.validation_error, dschema.TypeValidationError)) with self.assertRaises(dschema.SchemaDefaultError) as err: v.validate({}) self.assertTrue( isinstance(err.exception.validation_error, dschema.TypeValidationError)) v.schema = { 'a': { 'b': { 'c': { dschema.Default: { 'd': 4 }, "d": dschema.prop(default=3) } }, } } r = v.validate({}, namespace=True) self.assertTrue(r.a.b.c.d, 4) v.schema = { 'a': { 'b': { 'c': { dschema.Default: { 'd': 4 }, "d": dschema.prop(default=3) } }, } } r = v.validate({}) self.assertTrue(r['a']['b']['c']['d'], 4) v.schema = { 'a': { 'b': { 'c': { dschema.Default: { 'd': 'notint' }, "d": dschema.prop(type=int) } }, } } with self.assertRaises(dschema.SchemaDefaultError) as err: v.validate({}, namespace=True) self.assertTrue( isinstance(err.exception.validation_error, dschema.TypeValidationError)) with self.assertRaises(dschema.SchemaDefaultError) as err: v.validate({}) self.assertTrue( isinstance(err.exception.validation_error, dschema.TypeValidationError))
def test_default_values(self): validator = dschema.Validator({ 'a': { 'b': { 'c': dschema.prop(default='d') }, }, 'e': { 'f': { '@default': 'g' } }, 'h': { '@default': 'i' }, 'j': dschema.prop(default={'test': 1}, dict=True), 'l': { 'm': dschema.prop(default={'test2': 2}, dict=True) }, 'o': { 'p': { 'q': dschema.prop(default={'test3': 3}) }, }, 'r': { 's': { dschema.Default: { 't': 'u' }, dschema.Dict: True, 't': 'a' } } }) result = validator.validate({}, namespace=True) self.assertTrue(hasattr(result, 'a')) self.assertTrue(hasattr(result.a, 'b')) self.assertTrue(hasattr(result.a.b, 'c')) self.assertEqual(result.a.b.c, 'd') self.assertTrue(hasattr(result, 'e')) self.assertTrue(hasattr(result.e, 'f')) self.assertEqual(result.e.f, 'g') self.assertEqual('i', result.h) self.assertTrue(hasattr(result, 'h')) self.assertTrue(hasattr(result, 'j')) self.assertTrue(hasattr(result, 'l')) self.assertTrue(hasattr(result.l, 'm')) self.assertDictEqual({'test': 1}, result.j) self.assertDictEqual({'test2': 2}, result.l.m) self.assertTrue(hasattr(result, 'o')) self.assertTrue(hasattr(result.o, 'p')) self.assertTrue(hasattr(result.o.p, 'q')) self.assertTrue(hasattr(result.o.p.q, 'test3')) self.assertEqual(result.o.p.q.test3, 3) self.assertTrue(hasattr(result, 'r')) self.assertTrue(hasattr(result.r, 's')) self.assertDictEqual(result.r.s, {'t': 'u'}) result = validator.validate({'r': {'s': {'t': 'a'}}}, namespace=True) self.assertTrue(hasattr(result, 'r')) self.assertTrue(hasattr(result.r, 's')) self.assertDictEqual(result.r.s, {'t': 'a'}) # result = validator.validate({'h': None}) self.assertIn('a', result) self.assertIn('b', result['a']) self.assertIn('c', result['a']['b']) self.assertEqual('d', result['a']['b']['c']) self.assertIn('e', result) self.assertIn('f', result['e']) self.assertEqual('g', result['e']['f']) self.assertEqual('i', result['h']) self.assertDictEqual({'test': 1}, result['j']) self.assertDictEqual({'test2': 2}, result['l']['m']) self.assertIn('r', result) self.assertIn('s', result['r']) self.assertIn('t', result['r']['s']) self.assertDictEqual(result['r']['s'], {'t': 'u'}) result = validator.validate({'r': {'s': {'t': 'a'}}}) self.assertIn('r', result) self.assertIn('s', result['r']) self.assertIn('t', result['r']['s']) self.assertDictEqual(result['r']['s'], {'t': 'a'})
import dschema # specifying a required property as # having a default value. schema = {'bad': dschema.prop(required=True, default='cant-have-both')} try: dschema.Validator(schema).validate({'bad': 'stuff'}) except dschema.SchemaError as e: # Message about 'required' and 'default' being mutually exclusive print(e) # Not providing a validation handler # for a type specified as a string schema = {'no_type_validator': dschema.prop(required=True, type='int')} try: validator = dschema.Validator(schema) # Validator.add_type must be used to add # something that handles 'int' ... # validator.add_type('int', int) validator.validate({'no_type_validator': 1}) except dschema.SchemaMissingTypeError as e:
def phone_type(number): # Exceptions are validation errors # Very similar design to the "argparse" module return phonenumbers.parse(number) def ssn_type(ssn): if re.match('^\d{3}-?\d{2}-?\d{4}$', ssn): return ssn else: raise ValueError('"{}" is not a valid SSN.') schema = { 'person': { 'first_name': dschema.prop(required=True), 'last_name': dschema.prop(required=True), 'phone': dschema.prop(required=True, type=phone_type), 'ssn': dschema.prop(required=True, type='ssn_type'), dschema.Required: True # "person" namespace is required, you must specify # even if "person" itself contains required properties }, # Allow a raw dictionary value to pass through 'other_info': dschema.prop(default=dict(), dict=True), # default to False if not present 'subscribed': dschema.prop(default=False, type=bool) }
def __init__(self, path: str): self.config_path = path def regex_type(value): return re.compile(value) def workers_type(value): try: value = int(value) except Exception: raise ValueError('Must be an integer value.') if value < 1: raise ValueError('Count must be at least 1.') return value self._validator = dschema.Validator({ 'api_key': { 'id': dschema.prop(required=True, type=int), 'hash': dschema.prop(required=True), dschema.Required: True }, 'session_path': dschema.prop(default='tgminer'), 'data_dir': dschema.prop(default='data'), 'chat_stdout': dschema.prop(default=False, type=bool), 'timestamp_format': dschema.prop(default='({:%Y/%m/%d - %I:%M:%S %p})'), 'group_filters': { 'title': dschema.prop(default='.*', type=regex_type), 'title_slug': dschema.prop(default='.*', type=regex_type), 'id': dschema.prop(default='.*', type=regex_type), 'username': dschema.prop(default='.*', type=regex_type), 'user_alias': dschema.prop(default='.*', type=regex_type), 'user_id': dschema.prop(default='.*', type=regex_type) }, 'direct_chat_filters': { 'username': dschema.prop(default='.*', type=regex_type), 'alias': dschema.prop(default='.*', type=regex_type), 'id': dschema.prop(default='.*', type=regex_type) }, 'user_filters': { 'username': dschema.prop(default='.*', type=regex_type), 'alias': dschema.prop(default='.*', type=regex_type), 'id': dschema.prop(default='.*', type=regex_type) }, 'download_photos': dschema.prop(default=True, type=bool), 'download_documents': dschema.prop(default=True, type=bool), 'download_stickers': dschema.prop(default=True, type=bool), 'download_animations': dschema.prop(default=True, type=bool), 'download_videos': dschema.prop(default=True, type=bool), 'download_video_notes': dschema.prop(default=True, type=bool), 'download_voice': dschema.prop(default=True, type=bool), 'download_audio': dschema.prop(default=True, type=bool), 'write_raw_logs': dschema.prop(default=True, type=bool), 'docname_filter': dschema.prop(default='.*', type=regex_type), 'log_direct_chats': dschema.prop(default=True, type=bool), 'log_group_chats': dschema.prop(default=True, type=bool), 'download_workers': dschema.prop(default=4, type=workers_type), 'updates_workers': dschema.prop(default=1, type=workers_type), 'log_update_threads': dschema.prop(default=False, type=bool) }) self._config = None self.load()
import dschema validator = dschema.Validator({ 'app_auth': { 'id': dschema.prop(required=True), 'token': dschema.prop(required=True), dschema.Required: True # You must specify a namespace as required # if you want it to be required, even if it # contains required properties }, 'integer': dschema.prop(required=True, type=int) }) # Handling of missing required values # =================================== data = { 'app_auth': { 'token': 'somerandomthing', }, 'integer': 1 } try: validator.validate(data) except dschema.MissingKeyError as e: # message about 'app_auth.id' being required but missing... print(e) data = {'integer': 1}