Package: okschema
# Validate JSON data using a schema written in Python# Examples```from okschema import validate, ValidationError, SchemaErrortry:validated_data = validate(schema, data)except ValidationError as e:print(e.js)  # {'code': ValidationCode.<xxx>}```## Validation of a simple form```from okschema import validate, fmt_uuiddef val_email(v):return vschema = {'my_password':  {'@t': 'string', '@lteq': 100},'user_id':      {'@t': 'string', '@regexp': fmt_uuid},'new_email':    {'@t': 'string', '@val': val_email},'new_password': {'@t': 'string', '@lteq': 100},}data = {'my_password': 'abc','user_id': 'dbc8911c-92e8-4cdb-85b8-47a7a6a82db1','new_email': 'abc@example.com','new_password': 'abc'}validated_data = validate(schema, data)```## Potential use in request handling```@api_request(schema=lambda m: {'email':    {'@t': 'string', '@lteq': m.user.c.email.ui_len},'password': {'@t': 'string', '@lteq': m.user.c.password.ui_len},'key':      {'@t': 'string', '@lteq': 10240},'permission_level': {'@t': 'int', '@in': [PermissionE.ADMIN, PermissionE.USER]},})async def add_user_account(request, m):...```## Nested structure - error handling```schemaA = {'outer': {'a': 'int','b': {'@t': 'str', '@lt': 2, '@optional': True, '@null': True, '@blank': True},'c': [{'@t': 'str', '@in': ['x', 'y', 'z']}]},'is_ok': {'@type': 'bool', '@default': True}}data1 = {}data2 = {'outer': {'a': 'xxx','b': '3333','c': ['a', 'x', 12]},'is_ok': True}``````>>> validate(schemaA, data1)ValidationError: ({'outer': {'code': ValidationCode.MISSING},'is_ok': {'code': ValidationCode.MISSING}}, schemaA)``````>>> validate(schemaA, data2)ValidationError: ({'outer': {'a': {'code': ValidationCode.BAD_TYPE},'b': {'code': ValidationCode.NOT_LT, 'details': 2},'c': [{'code': ValidationCode.NOT_IN},None,  # no error here{'code': ValidationCode.BAD_TYPE}]},'is_ok': {'code': ValidationCode.BAD_TYPE}), schemaA)```## Custom validators - error handling```def bad_val1_cont(x):raise NotValidButContinueError(ValidationCode.BAD_VALUE, 1)def bad_val2_cont(x):raise NotValidButContinueError(ValidationCode.BAD_VALUE, 5)def bad_val3(x):raise NotValidError(ValidationCode.BAD_VALUE)schemaA = {'b':  {'@t': 'int', '@val': [bad_val1_cont, bad_val2_cont]}}schemaB = {'b':  {'@t': 'int', '@val': [bad_val1_cont, bad_val2_cont, bad_val3]}}data1 = {'b': 12}``````>>> validate(schemaA, data1)ValidationError: ({'b': {'code': ValidationCode.MANY_ERRORS, 'details': [{'code': ValidationCode.BAD_VALUE, 'details': 1},{'code': ValidationCode.BAD_VALUE, 'details': 5}]}}, schemaA)>>> validate(schemaB, data1)ValidationError: ({'b': {'code': ValidationCode.MANY_ERRORS, 'details': [{'code': ValidationCode.BAD_VALUE, 'details': 1},{'code': ValidationCode.BAD_VALUE, 'details': 5},{'code': ValidationCode.BAD_VALUE}]}}, schemaA)```# ValidationCode enumBAD_TYPE = 1NOT_IN = 2NULL = 3MISSING = 4OUT_OF_BOUNDS = 5  # for use by validatorsREGEXP = 6MANY_ERRORS = 8  # list of errors for a single fieldNOT_GT = 9NOT_GTEQ = 10NOT_LT = 11NOT_LTEQ = 12NOT_EQ = 13# Field description```"field: "type""field": { extended field description }```# Extended field descriptionBy default all fields are required and strings must be non-empty. Extra fields in input data are discarded.```{"@t": "type", # default type is dict# -- validators --# Checks that regexp matches - called before other validators."@regexp": "regexp",# Validator function."@val": val_fun,"@val: [val_fun, val_fun2, ...],# -- options --# If True and value is missing, it will not be present in result unless# default is defined. Makes sense for subfields in dicts."@optional": bool,# Allows empty strings."@blank": bool,# Used when value is not present.# Default value is never passed to validators."@default": value,# Allow nulls (None). By default null is not allowed."@null": bool,# -- limits --# Checked before validators are called.# They work for string lengths too.# TODO: They work for list lengths too."@in": [value1, value2, ...],"@gt": value,"@gteq": value,"@lt": value,"@lteq": value,"@neq": value,# Extra fields if type is dict."field1": field description or extended field description,...}```# Lists.```"field": [field description]"field": [{extended field description}]```## TODO: Optional lists.## TODO: List length limits.```"field": [{# field description}, {# list parameters"@optional": True,"@gt": val,}]```# Available types## scalar- string (str)- int- decimal- float- bool## composite- dict## TODO: Type "any" handles any type of subjson withot further validation# Validator functions```def fun(val):return val + 1```Should either return validated value or raise NotValidError(code, details).May also raise NotValidButContinueError to store the error but call the next validator, constructing a listof errors.