schemable

version travis coveralls license

Schemable is a schema parsing and validation library that let’s you define schemas simply using dictionaries, lists, types, and callables.

Features

  • Simple schema definitions using dict, list, and type objects
  • Complex schema definitions using Any, All, As, and predicates
  • Detailed validation error messages
  • Partial data loading on validation failure
  • Strict and non-strict parsing modes
  • Python 3.4+

Quickstart

Install using pip:

pip install schemable

Define a schema using dict and list objects:

from schemable import Schema, All, Any, As, Optional, SchemaError

user_schema = Schema({
    'name': str,
    'email': All(str, lambda email: len(email) > 3 and '@' in email),
    'active': bool,
    'settings': {
        Optional('theme'): str,
        Optional('language', default='en'): str,
        Optional('volume'): int,
        str: str
    },
    'aliases': [str],
    'phone': All(str,
                 As(lambda phone: ''.join(filter(str.isdigit, phone))),
                 lambda phone: 10 <= len(phone) <= 15),
    'addresses': [{
        'street_addr1': str,
        Optional('street_addr2', default=None): Any(str, None),
        'city': str,
        'state': str,
        'country': str,
        'zip_code': str
    }]
})

Then validate and load by passing data to user_schema():

# Fail!
result = user_schema({
    'name': 'Bob Smith',
    'email': 'bob.example.com',
    'active': 1,
    'settings': {
        'theme': False,
        'extra_setting1': 'val1',
        'extra_setting2': True
    },
    'phone': 1234567890,
    'addresses': [
        {'street_addr1': '123 Lane',
         'city': 'City',
         'state': 'ST',
         'country': 'US',
         'zip_code': 11000}
    ]
})

print(result)
# SchemaResult(
#     data={'name': 'Bob Smith',
#           'settings': {'extra_setting1': 'val1',
#                        'language': 'en'}
#           'addresses': [{'street_addr1': '123 Lane',
#                          'city': 'City',
#                          'state': 'ST',
#                          'country': 'US',
#                          'street_addr2': None}]},
#     errors={'email': "bad value: <lambda>('bob.example.com') should evaluate to True",
#             'active': 'bad value: type error, expected bool but found int',
#             'settings': {'theme': 'bad value: type error, expected str but found bool',
#                          'extra_setting2': 'bad value: type error, expected str but found bool'},
#             'phone': 'bad value: type error, expected str but found int',
#             'addresses': {0: {'zip_code': 'bad value: type error, expected str but found int'}},
#             'aliases': 'missing required key'})

# Fail!
result = user_schema({
    'name': 'Bob Smith',
    'email': 'bob@example.com',
    'active': True,
    'settings': {
        'theme': False,
        'extra_setting1': 'val1',
        'extra_setting2': 'val2'
    },
    'phone': '123-456-789',
    'addresses': [
        {'street_addr1': '123 Lane',
         'city': 'City',
         'state': 'ST',
         'country': 'US',
         'zip_code': '11000'}
    ]
})

print(result)
# SchemaResult(
#     data={'name': 'Bob Smith',
#           'email': 'bob@example.com',
#           'active': True,
#           'settings': {'extra_setting1': 'val1',
#                        'extra_setting2': 'val2',
#                        'language': 'en'},
#           'addresses': [{'street_addr1': '123 Lane',
#                          'city': 'City',
#                          'state': 'ST',
#                          'country': 'US',
#                          'zip_code': '11000',
#                          'street_addr2': None}]},
#     errors={'settings': {'theme': 'bad value: type error, expected str but found bool'},
#             'phone': "bad value: <lambda>('123456789') should evaluate to True",
#             'aliases': 'missing required key'})

Or can raise an exception on validation failure instead of returning results:

# Fail strictly!
try:
    user_schema({
        'name': 'Bob Smith',
        'email': 'bob@example.com',
        'active': True,
        'settings': {
            'theme': False,
            'extra_setting1': 'val1',
            'extra_setting2': 'val2'
        },
        'phone': '123-456-789',
        'addresses': [
            {'street_addr1': '123 Lane',
             'city': 'City',
             'state': 'ST',
             'country': 'US',
             'zip_code': '11000'}
        ]
    }, strict=True)
except SchemaError as exc:
    print(exc)
    # Schema validation failed: \
    # {'settings': {'theme': 'bad value: type error, expected str but found bool'}, \
    # 'phone': "bad value: <lambda>('123456789') should evaluate to True", \
    # 'aliases': 'missing required key'}

Access the parsed data after successful validation:

# Pass!
result = user_schema({
    'name': 'Bob Smith',
    'email': 'bob@example.com',
    'active': True,
    'settings': {
        'theme': 'dark',
        'extra_setting1': 'val1',
        'extra_setting2': 'val2'
    },
    'phone': '123-456-7890',
    'aliases': [],
    'addresses': [
        {'street_addr1': '123 Lane',
         'city': 'City',
         'state': 'ST',
         'country': 'US',
         'zip_code': '11000'}
    ]
})

print(result)
# SchemaResult(
#     data={'name': 'Bob Smith',
#           'email': 'bob@example.com',
#           'active': True,
#           'settings': {'theme': 'dark',
#                        'extra_setting1': 'val1',
#                        'extra_setting2': 'val2',
#                        'language': 'en'},
#           'phone': '1234567890',
#           'aliases': [],
#           'addresses': [{'street_addr1': '123 Lane',
#                          'city': 'City',
#                          'state': 'ST',
#                          'country': 'US',
#                          'zip_code': '11000',
#                          'street_addr2': None}]},
#     errors={})

For more details, please see the full documentation at https://schemable.readthedocs.io.

Indices and Tables