Simple validation tool for API
Project description
data-spec-validator
Why
- To get rid of code snippet like these (... cumbersome and tedious validation)
def do_something(params):
val_a_must_int = params.get('a', 0)
val_b_must_be_non_empty_list = params.get('b', [])
# if key c presents, value c must be a date string between '2000-01-01' to '2020-01-01'
val_c_might_be_none = params.get('c', None)
# check type
if type(val_a_must_int) != int:
raise XXX
# check type & value
if type(val_b_must_list) != list or len(val_b_must_be_non_empty_list) == 0:
raise XXX
# if value exists, check its value
if val_c_might_be_none is not None:
date_c = datetime.strptime(val_c_might_be_present, '%Y-%m-%d')
date_20000101 = datetime.date(2000, 1, 1)
date_20200101 = datetime.date(2020, 1, 1)
if not (date_20000101 <= date_c <= date_20200101):
raise XXX
...
# do something actually
Installation
- Basic usage:
pip install data-spec-validator
- Advance usage (decorator)
- The decorator function
dsv
depends onDjango
&djangorestframework
.
- The decorator function
pip install data-spec-validator[decorator]
Quick Example
- Do
validate_data_spec
directly wherever you like
from data_spec_validator.spec import INT, DIGIT_STR, ONE_OF, Checker, CheckerOP, validate_data_spec
class SomeSpec:
field_a = Checker([INT])
field_b = Checker([DIGIT_STR], optional=True)
field_c = Checker([DIGIT_STR, INT], op=CheckerOP.ANY)
some_data = dict(field_a=4, field_b='3', field_c=1, field_dont_care=[5,6])
validate_data_spec(some_data, SomeSpec) # return True
some_data = dict(field_a=4, field_c='1')
validate_data_spec(some_data, SomeSpec) # return True
some_data = dict(field_a=4, field_c=1)
validate_data_spec(some_data, SomeSpec) # return True
some_data = dict(field_a='4', field_c='1')
validate_data_spec(some_data, SomeSpec) # raise Exception
some_data = dict(field_a='4', field_c='1')
validate_data_spec(some_data, SomeSpec, nothrow=True) # return False
class AnotherSpec:
field = Checker([ONE_OF], ONE_OF=[1, '2', [3, 4], {'5': 6}])
another_data = dict(field=[3, 4])
validate_data_spec(another_data, AnotherSpec) # return True
another_data = dict(field='4')
validate_data_spec(another_data, AnotherSpec) # raise Exception
- Supported checks & sample usages (see
test_spec.py
for more cases)
### INT
int_field = Checker([INT])
### STR
str_field = Checker([STR])
### DIGIT_STR
digi_str_field = Checker([DIGIT_STR])
### BOOL
bool_field = Checker([BOOL])
### DICT
dict_field = Checker([DICT])
### LIST
list_field = Checker([LIST])
### NONE
none_field = Checker([NONE])
### JSON
none_field = Checker([JSON])
### JSON_BOOL
none_field = Checker([JSON_BOOL])
### ONE_OF
one_of_field = Checker([ONE_OF], ONE_OF=['a', 'b', 'c'])
### SPEC
spec_field = Checker([SPEC], SPEC=SomeSpecClass)
### LIST_OF
list_of_int_field = Checker([LIST_OF], LIST_OF=INT)
list_of_spec_field = Checker([LIST_OF], LIST_OF=SomeSpecClass)
### LENGTH
length_field = Checker([LENGTH], LENGTH=dict(min=3, max=5))
### AMOUNT
amount_field = Checker([AMOUNT])
### AMOUNT_RANGE
amount_range_field = Checker([AMOUNT_RANGE], AMOUNT_RANGE=dict(min=-2.1, max=3.8))
### DECIMAL_PLACE
decimal_place_field = Checker([DECIMAL_PLACE], DECIMAL_PLACE=4)
### DATE
date_field = Checker([DATE])
### DATE_RANGE
date_range_field = Checker([DATE_RANGE], DATE_RANGE=dict(min='2000-01-01', max='2010-12-31'))
### ANY_KEY_EXISTS
any_key_checker = Checker([ANY_KEY_EXISTS], ANY_KEY_EXISTS={'key1', 'key2', 'key3'})
### KEY_COEXISTS
key1 = Checker([KEY_COEXISTS], KEY_COEXISTS=['key2'])
### EMAIL
email_field = Checker([EMAIL])
### UUID
uuid_field = Checker([UUID])
### REGEX
re_field = Checker([REGEX], REGEX=dict(pattern=r'^The'))
re_field = Checker([REGEX], REGEX=dict(pattern=r'watch out', method='match'))
### COND_EXIST
# 1. If a exists, c must not exist, if b exists, a must exist, if c exists, a must not exist.
a = Checker([COND_EXIST], optional=True, COND_EXIST=dict(WITHOUT=['c']))
b = Checker([COND_EXIST], optional=True, COND_EXIST=dict(WITH=['a']))
c = Checker([COND_EXIST], optional=True, COND_EXIST=dict(WITHOUT=['a']))
- Decorate a method with
dsv
, the method must meet one of the following requirements.- It's a view's member function, and the view has a WSGIRequest(
django.core.handlers.wsgi.WSGIRequest
) attribute. - It's a view's member function, and the 2nd argument of the method is a
rest_framework.request.Request
instance. - It's already decorated with
rest_framework.decorators import api_view
, the 1st argument is arest_framework.request.Request
- It's a view's member function, and the view has a WSGIRequest(
from rest_framework.decorators import api_view
from rest_framework.views import APIView
from data_spec_validator.decorator import dsv
from data_spec_validator.spec import UUID, EMAIL, Checker
class SomeViewSpec:
param_a = Checker([UUID])
param_b = Checker([EMAIL])
class SomeView(APIView):
@dsv(SomeViewSpec)
def get(self, request):
pass
@api_view(('POST',))
@dsv(SomeViewSpec)
def customer_create(request):
pass
- Decorate another method with
dsv_request_meta
can help you validate the META in request header.
Register Custom Spec Check & Validator
- Define custom CHECK constant (
gt_check
in this case) and write custom Validator(GreaterThanValidator
in this case)
gt_check = 'gt_check'
from data_spec_validator.spec.defines import BaseValidator
class GreaterThanValidator(BaseValidator):
name = gt_check
@staticmethod
def validate(value, extra, data):
criteria = extra.get(GreaterThanValidator.name)
return value > criteria, ValueError(f'{value} is not greater than {criteria}')
- Register custom check & validator into data_spec_validator
from data_spec_validator.spec import custom_spec, Checker, validate_data_spec
custom_spec.register(dict(gt_check=GreaterThanValidator()))
class GreaterThanSpec:
key = Checker(['gt_check'], GT_CHECK=10)
ok_data = dict(key=11)
validate_data_spec(ok_data, GreaterThanSpec) # return True
nok_data = dict(key=9)
validate_data_spec(ok_data, GreaterThanSpec) # raise Exception
Message Level
- 2 modes (Default v.s. Vague), can be switched by calling
reset_msg_level(vague=True)
# In default mode, any exception happens, there will be a reason in the message
"field: XXX, reason: '3' is not a integer"
# In vague mode, any exception happens, a general message is shown
"field: XXX not well-formatted"
Feature: Strict Mode
- A spec class decorated with
dsv_feature(strict=True)
detects unexpected key/value in data
from data_spec_validator.spec import Checker, validate_data_spec, dsv_feature, BOOL
@dsv_feature(strict=True)
class StrictSpec:
a = Checker([BOOL])
ok_data = dict(a=True)
validate_data_spec(ok_data, StrictSpec) # return True
nok_data = dict(a=True, b=1)
validate_data_spec(nok_data, StrictSpec) # raise Exception
Feature: Any Keys Set
- A spec class decorated with e.g.
dsv_feature(any_keys_set={...})
means that at least one key among a keys tuple from the set must exist.
from data_spec_validator.spec import Checker, validate_data_spec, dsv_feature, INT
@dsv_feature(any_keys_set={('a', 'b'), ('c', 'd')})
class _AnyKeysSetSpec:
a = Checker([INT], optional=True)
b = Checker([INT], optional=True)
c = Checker([INT], optional=True)
d = Checker([INT], optional=True)
validate_data_spec(dict(a=1, c=1, d=1), _AnyKeysSetSpec)
validate_data_spec(dict(a=1, c=1), _AnyKeysSetSpec)
validate_data_spec(dict(a=1, d=1), _AnyKeysSetSpec)
validate_data_spec(dict(b=1, c=1, d=1), _AnyKeysSetSpec)
validate_data_spec(dict(b=1, c=1), _AnyKeysSetSpec)
validate_data_spec(dict(b=1, d=1), _AnyKeysSetSpec)
validate_data_spec(dict(a=1, b=1, c=1), _AnyKeysSetSpec)
validate_data_spec(dict(a=1, b=1, d=1), _AnyKeysSetSpec)
validate_data_spec(dict(a=1, b=1, c=1, d=1), _AnyKeysSetSpec)
validate_data_spec(dict(a=1), _AnyKeysSetSpec) # raise exception
validate_data_spec(dict(b=1), _AnyKeysSetSpec) # raise exception
validate_data_spec(dict(c=1), _AnyKeysSetSpec) # raise exception
validate_data_spec(dict(d=1), _AnyKeysSetSpec) # raise exception
validate_data_spec(dict(e=1), _AnyKeysSetSpec) # raise exception
Test
python -m unittest test.test_spec
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
data-spec-validator-1.7.0.tar.gz
(24.2 kB
view details)
File details
Details for the file data-spec-validator-1.7.0.tar.gz
.
File metadata
- Download URL: data-spec-validator-1.7.0.tar.gz
- Upload date:
- Size: 24.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.4.2 importlib_metadata/4.10.1 pkginfo/1.8.2 requests/2.27.1 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.10.2
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 938584b5807c030b6c5de4fa84082f5eb3ea2451e8d64b3109d643bc1578171c |
|
MD5 | 019dc74b24292b5c13859e066287648f |
|
BLAKE2b-256 | cd4bc87af7428bffd58f9053ac71273c9a88969ccdcd103d44d804ddec9f9875 |