async def test_error_reporting(): s = Schema( { '<files>': [Use(open, error='<files> should be readable')], '<path>': And(os.path.exists, error='<path> should exist'), '--count': Or(None, And(Use(int), lambda n: 0 < n < 5), error='--count should be integer 0 < n < 5') }, error='Error:') await s.validate({'<files>': [], '<path>': './', '--count': 3}) try: await s.validate({'<files>': [], '<path>': './', '--count': '10'}) except SchemaError as e: assert e.code == 'Error:\n--count should be integer 0 < n < 5' try: await s.validate({'<files>': [], '<path>': './hai', '--count': '2'}) except SchemaError as e: assert e.code == 'Error:\n<path> should exist' try: await s.validate({'<files>': ['hai'], '<path>': './', '--count': '2'}) except SchemaError as e: assert e.code == 'Error:\n<files> should be readable'
async def test_schema(): assert await Schema(1).validate(1) == 1 with SE: await Schema(1).validate(9) assert await Schema(int).validate(1) == 1 with SE: await Schema(int).validate('1') assert await Schema(Use(int)).validate('1') == 1 with SE: await Schema(int).validate(int) assert await Schema(str).validate('hai') == 'hai' with SE: await Schema(str).validate(1) assert await Schema(Use(str)).validate(1) == '1' assert await Schema(list).validate(['a', 1]) == ['a', 1] assert await Schema(dict).validate({'a': 1}) == {'a': 1} with SE: await Schema(dict).validate(['a', 1]) assert await Schema(lambda n: 0 < n < 5).validate(3) == 3 with SE: await Schema(lambda n: 0 < n < 5).validate(-1)
async def test_and(): assert await And(int, lambda n: 0 < n < 5).validate(3) == 3 with SE: await And(int, lambda n: 0 < n < 5).validate(3.33) assert await And(Use(int), lambda n: 0 < n < 5).validate(3.33) == 3 with SE: await And(Use(int), lambda n: 0 < n < 5).validate('3.33') with raises(TypeError): await And(str, len, hello='a').validate('hello')
async def test_issue_9_prioritized_key_comparison_in_dicts(): # http://stackoverflow.com/questions/14588098/docopt-schema-validation s = Schema({ 'ID': Use(int, error='ID should be an int'), 'FILE': Or(None, Use(open, error='FILE should be readable')), Optional(str): object }) data = {'ID': 10, 'FILE': None, 'other': 'other', 'other2': 'other2'} assert await s.validate(data) == data data = {'ID': 10, 'FILE': None} assert await s.validate(data) == data
async def test_validate_file(): result = await Schema(Use(open)).validate('LICENSE-MIT') assert result.read().startswith('Copyright') with SE: await Schema(Use(open)).validate('NON-EXISTENT') assert await Schema(os.path.exists).validate('.') == '.' with SE: await Schema(os.path.exists).validate('./non-existent/') assert await Schema(os.path.isfile ).validate('LICENSE-MIT') == 'LICENSE-MIT' with SE: await Schema(os.path.isfile).validate('NON-EXISTENT')
async def test_async(): async def func(n): return 0 < n < 5 assert await Schema(func).validate(3) == 3 with SE: await Schema(func).validate(-1) async def change(data): return str(data) assert await Schema(Use(change)).validate(1) == '1' assert await Schema(Use(change)).is_valid(1) == True assert await Schema(Use(str)).validate(1) == '1' assert await Schema(str).is_valid(1) == False
async def test_nice_errors(): try: await Schema(int, error='should be integer').validate('x') except SchemaError as e: assert e.errors == ['should be integer'] try: await Schema(Use(float), error='should be a number').validate('x') except SchemaError as e: assert e.code == 'should be a number' try: await Schema({ Optional('i'): Use(int, error='should be a number') }).validate({'i': 'x'}) except SchemaError as e: assert e.code == 'should be a number'
async def test_optional_key_convert_failed_randomly_while_with_another_optional_object( ): """ In this test, created_at string "2015-10-10 00:00:00" is expected to be converted to a datetime instance. - it works when the schema is s = Schema({ 'created_at': _datetime_validator, Optional(basestring): object, }) - but when wrapping the key 'created_at' with Optional, it fails randomly :return: """ import datetime fmt = '%Y-%m-%d %H:%M:%S' _datetime_validator = Or(None, Use(lambda i: datetime.datetime.strptime(i, fmt))) # FIXME given tests enough for _ in range(1024): s = Schema({ Optional('created_at'): _datetime_validator, Optional('updated_at'): _datetime_validator, Optional('birth'): _datetime_validator, Optional(basestring): object, }) data = {'created_at': '2015-10-10 00:00:00'} validated_data = await s.validate(data) # is expected to be converted to a datetime instance, but fails randomly # (most of the time) assert isinstance(validated_data['created_at'], datetime.datetime)
async def test_missing_keys_exception_with_non_str_dict_keys(): s = Schema({And(str, Use(str.lower), 'name'): And(str, len)}) with SE: await s.validate(dict()) with SE: try: await Schema({1: 'x'}).validate(dict()) except SchemaMissingKeyError as e: assert e.args[0] == "Missing keys: 1" raise
async def test_complex(): s = Schema({ '<file>': And([Use(open)], lambda l: len(l)), '<path>': os.path.exists, Optional('--count'): And(int, lambda n: 0 <= n <= 5) }) data = await s.validate({'<file>': ['./LICENSE-MIT'], '<path>': './'}) assert len(data) == 2 assert len(data['<file>']) == 1 assert data['<file>'][0].read().startswith('Copyright') assert data['<path>'] == './'
async def test_dict_keys(): assert await Schema({ str: int }).validate({ 'a': 1, 'b': 2 }) == { 'a': 1, 'b': 2 } with SE: await Schema({str: int}).validate({1: 1, 'b': 2}) assert await Schema({ Use(str): Use(int) }).validate({ 1: 3.14, 3.14: 1 }) == { '1': 3, '3.14': 1 }
async def test_schema_error_handling(): try: await Schema(Use(ve)).validate('x') except SchemaError as e: assert e.autos == [None, "ve('x') raised ValueError()"] assert e.errors == [None, None] try: await Schema(Use(ve), error='should not raise').validate('x') except SchemaError as e: assert e.autos == [None, "ve('x') raised ValueError()"] assert e.errors == ['should not raise', None] try: await Schema(Use(se)).validate('x') except SchemaError as e: assert e.autos == [None, None, 'first auto'] assert e.errors == [None, None, 'first error'] try: await Schema(Use(se), error='second error').validate('x') except SchemaError as e: assert e.autos == [None, None, 'first auto'] assert e.errors == ['second error', None, 'first error']
async def test_test(): def unique_list(_list): return len(_list) == len(set(_list)) def dict_keys(key, _list): return list(map(lambda d: d[key], _list)) schema = (Schema(Const(And(Use(partial(dict_keys, "index")), unique_list)))) data = [{"index": 1, "value": "foo"}, {"index": 2, "value": "bar"}] assert await schema.validate(data) == data bad_data = [{"index": 1, "value": "foo"}, {"index": 1, "value": "bar"}] with SE: await schema.validate(bad_data)
async def test_use_json(): import json gist_schema = Schema( And( Use(json.loads), # first convert from JSON { Optional('description'): basestring, 'public': bool, 'files': { basestring: { 'content': basestring } } })) gist = '''{"description": "the description for this gist", "public": true, "files": { "file1.txt": {"content": "String file contents"}, "other.txt": {"content": "Another file contents"}}}''' assert await gist_schema.validate(gist)
async def test_dict_optional_defaults(): # Optionals fill out their defaults: assert await Schema({ Optional('a', default=1): 11, Optional('b', default=2): 22 }).validate({'a': 11}) == { 'a': 11, 'b': 2 } # Optionals take precedence over types. Here, the "a" is served by the # Optional: assert await Schema({ Optional('a', default=1): 11, basestring: 22 }).validate({'b': 22}) == { 'a': 1, 'b': 22 } with raises(TypeError): Optional(And(str, Use(int)), default=7)
from schema_async import Schema, And, Use, Optional import asyncio schema = Schema([{ 'name': And(str, len), 'age': And(Use(int), lambda n: 18 <= n <= 99), Optional('gender'): And(str, Use(str.lower), lambda s: s in ('squid', 'kid')) }]) data = [{ 'name': 'Sue', 'age': '28', 'gender': 'Squid' }, { 'name': 'Sam', 'age': '42' }, { 'name': 'Sacha', 'age': '20', 'gender': 'KID' }] loop = asyncio.get_event_loop() validated = loop.run_until_complete(schema.validate(data)) print(validated) assert validated == [{
async def test_schema_repr(): # what about repr with `error`s? schema = Schema([Or(None, And(str, Use(float)))]) repr_ = "Schema([Or(None, And(<type 'str'>, Use(<type 'float'>)))])" # in Python 3 repr contains <class 'str'>, not <type 'str'> assert repr(schema).replace('class', 'type') == repr_