/simple_schema_validator

A dead-simple utility that validates if object has a certain structure. Used in some of our projects.

Primary LanguagePythonMIT LicenseMIT

Simple schema validator

coverage

A dead-simple utility that validates if object has a certain structure. Used in some of our projects.

Basic usage

pip install simple_schema_validator

An example:

Lets say we have an API that returns the following data:

{
  "user": 1,
  "profile": {
    "email": "some@user.com",
    "name": "Some User",
    "age": 20
  },
  "tokens": {
    "jwt": "...",
    "refresh": "...",
    "firebase": "...",
  }
}

And we are writing a simple integration test, that wants to assure the response has a certain structure.

Then we can use the schema validator like so:

from simple_schema_validator import schema_validator

data = get_data_from_api()

schema = {
  'user': Any,
  'profile': {
    'email': Any,
    'name': Any,
    'age': Any
  },
  'tokens': {
    'jwt': Any,
    'refresh': Any,
    'firebase': Any
  }
}

validation = schema_validator(schema, data)

if not validation:
    print(f'Keys in data, but not in schema: {validation.additional_keys}')
    print(f'Keys in schema, but not in data: {validation.missing_keys}')
    print(f'Keys with different type from schema {validation.type_errors}')
  • missing_keys are those keys that are required in the schema, but not found in data.
  • additional_keys are those keys present in data, but not required by the schema.
  • validation_errors are those keys, that are having a different type in data, from the defined in schema.

Nested keys are represented with "dot" notation - profile.email, tokens.jwt, etc.

Type checking

The util supports simple schema type checking.

Currently, the supported types in the schema are:

  • int
  • float
  • str
  • bool
  • typing.Any (from Python typing library)
  • simple_schema_validator.types.Optional (custom type, define in the package)

If the type is Any, no type checking is done.

If there's a type mismatch, the errors are placed in the type_errors attribute of the result, which is a list of type errors.

The general format of a single type error is:

{
  'path': 'the.path.to.the.value.in.data',
  'expected': the_expected_type_as_defined_in_the_schema,
  'actual': the_actual_type_of_the_value
}

Here's an example:

from simple_schema_validator import schema_validator, types


schema = {
  'user': str,
  'profile': {
    'email': str,
    'name': str,
    'age': int
  },
  'tokens': {
    'jwt': str,
    'refresh': str,
    'firebase': str
  }
}

data = {
  'user': 'Some User',
  'profile': {
    'email': 'someuser@hacksoft.io',
    'name': 'Some User',
    'age': "29"
  },
  'tokens': {
    'jwt': 'some token value',
    'refresh': 'some token value',
    'firebase': 'some token value'
  }

}

result = schema_validator(schema, data)


assert bool(result) is False
assert result.type_errors == [{'path': 'profile.age', 'expected': int, 'actual': str}]

Optional types

The schema validator support optional types.

You can do the following:

from simple_schema_validator import schema_validator, types

schema = {
  'a': types.Optional[int]
}

data_1 = {
  'a': None
}

data_2 = {
  'a': 1
}

data_3 = {
  'a': 'some_string'
}

assert bool(schema_validator(schema, data_1)) is True
assert bool(schema_validator(schema, data_2)) is True
assert bool(schema_validator(schema, data_3)) is False

Additionally, you can define optional branches in the schema:

from simple_schema_validator import schema_validator, types

schema = {
  'a': types.Optional[{
    'b': int
  }]
}

data_1 = {
  'a': None
}

data_2 = {
  'a': 1
}

data_3 = {
  'a': {
    'b': 1
  }
}

data_4 = {
  'a': {
    'b': 'some_string'
  }
}

assert bool(schema_validator(schema, data_1)) is True
assert bool(schema_validator(schema, data_2)) is False
assert bool(schema_validator(schema, data_3)) is True
assert bool(schema_validator(schema, data_4)) is False

You can use it with optional lists aswell:

from simple_schema_validator import schema_validator, types

schema = {
  'a': types.Optional[[int]]
}

data_1 = {
  'a': None
}

data_2 = {
  'a': 1
}

data_3 = {
  'a': [1, 2, 3]
}

data_4 = {
  'a': ['some_string']
}

assert bool(schema_validator(schema, data_1)) is True
assert bool(schema_validator(schema, data_2)) is False
assert bool(schema_validator(schema, data_3)) is True
assert bool(schema_validator(schema, data_4)) is False

List types

The schema validator support list types.

You can do the following:

from simple_schema_validator import schema_validator, types

schema = {
  'a': types.List[int]
}

data_1 = {
  'a': [1, 2, 3]
}

data_3 = {
  'a': ['some_string']
}

assert bool(schema_validator(schema, data_1)) is True
assert bool(schema_validator(schema, data_2)) is False

Recursive schemas

The schema validator support type checking for schemas in list.

You can do the following:

from simple_schema_validator import schema_validator, types

schema = {
  'a': [{'b': int}]
}

data_1 = {
  'a': [{'b': 1}]
}

data_2 = {
  'a': [{'b': 'some_string'}]
}

assert bool(schema_validator(schema, data_1)) is True
assert bool(schema_validator(schema, data_2)) is False
from simple_schema_validator import schema_validator, types

schema = {
  'a': [{'b': [int]}]
}

data_1 = {
  'a': [{'b': [1]}]
}

data_2 = {
  'a': [{'b': [1, 2, 3]}]
}

data_3 = {
  'a': [{'b': ['some_string']}]
}

data_4 = {
  'a': [{'b': [1, 'some_string']}]
}

data_5 = {
  'a': [{'b': ['some_string', 'other_string']}]
}

data_6 = {
  'a': [{'b': ['some_string', 1]}]
}

assert bool(schema_validator(schema, data_1)) is True
assert bool(schema_validator(schema, data_2)) is True
assert bool(schema_validator(schema, data_3)) is False
assert bool(schema_validator(schema, data_4)) is False
assert bool(schema_validator(schema, data_5)) is False
assert bool(schema_validator(schema, data_6)) is False

You can do the same with optional branches aswell:

from simple_schema_validator import schema_validator, types

schema = {
  'a': [{'b': types.Optional[int]}]
}

data_1 = {
  'a': [{
    'b': {
      'c': 1,
      'd': 2
    }
  }]
}

data_2 = {
  'a': [{'b': 'some_string'}]
}

assert bool(schema_validator(schema, data_1)) is True
assert bool(schema_validator(schema, data_2)) is False

Examples

For examples, check the examples folder or the tests for the project.