Skip to content

Commit 8bdf417

Browse files
authored
Merge pull request #6 from Attumm/towards_version_three
version three
2 parents c083fdb + 4d08c96 commit 8bdf417

File tree

10 files changed

+367
-172
lines changed

10 files changed

+367
-172
lines changed

.coveragerc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ source =
99
omit =
1010
setup.py
1111
*test*
12+
maat/version.py
1213

maat/__init__.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
from .maat import scale, validate
22
from .exceptions import Invalid
33

4-
from .validations import registered_functions
4+
from .validations import types
55
from .validations import int_validation, str_validation, float_validation, list_validation, dict_validation
66

77
from .transformations import registered_transformation
88

99
from .extras import protected
10+
from .version import VERSION as version
1011

11-
__all__ = ['scale', 'protected', 'Invalid', 'registered_functions', 'registered_transformation',
12+
__all__ = ['scale', 'protected', 'Invalid', 'types', 'registered_transformation',
1213
'int_validation', 'str_validation', 'float_validation', 'list_validation', 'dict_validation', 'uuid_validation'
13-
'validate']
14+
'validate', 'version']

maat/maat.py

Lines changed: 63 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,24 @@
1-
from .validations import registered_functions
1+
from .validations import types
22
from .transformations import registered_transformation
33
from .exceptions import Invalid
44

55
NESTED = "nested"
6-
TYPELIST = "list"
7-
VALIDATOR = "type"
86
OPTIONAL = "optional"
9-
NULLABLE = "null_able"
10-
TYPEASOARR = "aso_array"
11-
SKIPFAILED = "skip_failed"
12-
TYPELISTDICTS = "list_dicts"
7+
NULLABLE = "nullable"
138
DEFAULT = "default"
149
EMPTYLIST = "empty_list"
1510
TRANSFORM = "transform"
1611
PRETRANSFORM = "pre_transform"
12+
SKIPFAILED = "skip_failed"
1713

18-
special_arguments = {VALIDATOR, NESTED, TYPELIST, TYPEASOARR, SKIPFAILED, NULLABLE, OPTIONAL, DEFAULT, PRETRANSFORM,
19-
TRANSFORM, TYPELISTDICTS, EMPTYLIST}
20-
21-
22-
def get_validation_func(item):
23-
try:
24-
return registered_functions[item[VALIDATOR]]
25-
except KeyError:
26-
raise Invalid(f"{item.get(VALIDATOR)} is not registered as type")
27-
14+
TYPE = "type"
15+
TYPELIST = "list"
16+
TYPEDICT = "dict"
17+
TYPEASOARR = "aso_array"
18+
TYPELISTDICTS = "list_dicts"
2819

29-
def get_validation_args(item):
30-
return {k: v for k, v, in item.items() if k not in special_arguments}
20+
special_arguments = {NESTED, TYPELIST, TYPEASOARR, SKIPFAILED, NULLABLE, OPTIONAL, DEFAULT, PRETRANSFORM,
21+
TRANSFORM, TYPELISTDICTS, EMPTYLIST, TYPEDICT, TYPE}
3122

3223

3324
def get_transformation_func(item, type_transformation):
@@ -64,7 +55,6 @@ def scale(input_dict, counter_dict):
6455

6556
validated_items = {}
6657
for key, item in counter_dict.items():
67-
6858
try:
6959
val = input_dict[key]
7060
except KeyError:
@@ -76,40 +66,47 @@ def scale(input_dict, counter_dict):
7666
else:
7767
raise Invalid(f'key:"{key}" is not set')
7868

79-
# # if the value is None, check for default value or check if it was required
69+
# if the value is None check if value is allowed to be None
8070
if val is None and item.get(NULLABLE):
8171
validated_items[key] = None
8272
continue
8373

8474
try:
85-
validation_func = registered_functions[item[VALIDATOR]]
75+
validation_func = types[item[TYPE]]
8676
except KeyError:
87-
raise Invalid(f"{item.get(VALIDATOR)} is not registered as type")
88-
89-
validation_args = {k: v for k, v, in item.items() if k not in special_arguments}
77+
raise Invalid(f"{item.get(TYPE)} is not registered as type")
9078

9179
pre_transformation = get_transformation_func(item, PRETRANSFORM)
9280
post_transformation = get_transformation_func(item, TRANSFORM)
9381

9482
# the validation can be done on top level, life is good
9583
if NESTED not in item:
96-
validated_items[key] = post_transformation(validation_func(key=key, val=pre_transformation(val), **validation_args))
84+
validated_items[key] = post_transformation(validation_func(key=key, val=pre_transformation(val), **item))
85+
continue
86+
87+
type_ = item["type"]
88+
if type_ == TYPEDICT:
89+
validation_func(key=key, val=val, **item)
90+
validated_items[key] = post_transformation(scale(pre_transformation(input_dict[key]), counter_dict[key][NESTED]))
9791

98-
elif TYPELIST in item:
92+
elif type_ == TYPELIST:
93+
validation_func(key=key, val=val, **item)
9994
validated_items[key] = item.get(EMPTYLIST, []) if len(val) == 0 else []
10095

96+
try:
97+
validation_func = types[item[NESTED][TYPE]]
98+
except KeyError:
99+
raise Invalid(f"{item[NESTED].get(TYPE)} is not registered as type")
100+
101101
for nested_item in val:
102-
# within a list a item should be skipable
103102
try:
104-
validated_items[key].append(post_transformation(validation_func(key=key, val=pre_transformation(nested_item), **validation_args)))
103+
validated_items[key].append(post_transformation(validation_func(key=key, val=pre_transformation(nested_item), **item[NESTED])))
105104
except Invalid:
106105
if not item.get(SKIPFAILED):
107106
raise
108107

109-
# the item is nested with a list of dictionary items
110-
elif TYPELISTDICTS in item:
111-
112-
validation_func(key=key, val=val, **validation_args)
108+
elif type_ == TYPELISTDICTS:
109+
validation_func(key=key, val=val, **item)
113110
validated_items[key] = []
114111
for nested_item in val:
115112
try:
@@ -118,22 +115,20 @@ def scale(input_dict, counter_dict):
118115
if not item.get(SKIPFAILED):
119116
raise
120117

121-
# the item is nested. we have to start over to do the same the one level deeper
122-
elif not item.get(TYPEASOARR, False):
123-
validated_items[key] = post_transformation(scale(pre_transformation(input_dict[key]), counter_dict[key][NESTED]))
124-
125-
# the item is a "associative array" dictionary e.g keys are numerical indexes
126-
else:
127-
validation_func(key=key, val=val, **validation_args)
118+
elif type_ == TYPEASOARR:
119+
validation_func(key=key, val=val, **item)
128120

129121
for nested_key, nested_val in val.items():
130-
# make sure dictionary is present.
122+
# TODO this could be nicer
131123
if key not in validated_items:
132124
validated_items[key] = {}
133125
if nested_key not in validated_items[key]:
134126
validated_items[key][nested_key] = {}
135127

136-
validated_items[key][nested_key] = scale(nested_val, counter_dict[key][NESTED])
128+
validated_items[key][nested_key] = validate(nested_val, counter_dict[key][NESTED])
129+
130+
else:
131+
raise Invalid(f"type {type_} can't handle nested structures, use {TYPEASOARR}, {TYPELISTDICTS}, {TYPEDICT} instead")
137132

138133
return validated_items
139134

@@ -156,37 +151,44 @@ def validate(input_dict, counter_dict):
156151
else:
157152
raise Invalid(f'key:"{key}" is not set')
158153

159-
# # if the value is None, check for default value or check if it was required
154+
# if the value is None check if value is allowed to be None
160155
if val is None and item.get(NULLABLE):
161156
validated_items[key] = None
162157
continue
163158

164159
try:
165-
validation_func = registered_functions[item[VALIDATOR]]
160+
validation_func = types[item[TYPE]]
166161
except KeyError:
167-
raise Invalid(f"{item.get(VALIDATOR)} is not registered as type")
168-
169-
validation_args = {k: v for k, v, in item.items() if k not in special_arguments}
162+
raise Invalid(f"{item.get(TYPE)} is not registered as type")
170163

171164
# the validation can be done on top level, life is good
172165
if NESTED not in item:
173-
validated_items[key] = validation_func(key=key, val=val, **validation_args)
166+
validated_items[key] = validation_func(key=key, val=val, **item)
167+
continue
174168

175-
elif TYPELIST in item:
169+
type_ = item["type"]
170+
if type_ == TYPEDICT:
171+
validation_func(key=key, val=val, **item)
172+
validated_items[key] = validate(input_dict[key], counter_dict[key][NESTED])
173+
174+
elif type_ == TYPELIST:
175+
validation_func(key=key, val=val, **item)
176176
validated_items[key] = item.get(EMPTYLIST, []) if len(val) == 0 else []
177177

178+
try:
179+
validation_func = types[item[NESTED][TYPE]]
180+
except KeyError:
181+
raise Invalid(f"{item[NESTED].get(TYPE)} is not registered as type")
182+
178183
for nested_item in val:
179-
# within a list a item should be skipable
180184
try:
181-
validated_items[key].append(validation_func(key=key, val=nested_item, **validation_args))
185+
validated_items[key].append(validation_func(key=key, val=nested_item, **item[NESTED])) # ADD test item nested
182186
except Invalid:
183187
if not item.get(SKIPFAILED):
184188
raise
185189

186-
# the item is nested with a list of dictionary items
187-
elif TYPELISTDICTS in item:
188-
189-
validation_func(key=key, val=val, **validation_args)
190+
elif type_ == TYPELISTDICTS:
191+
validation_func(key=key, val=val, **item)
190192
validated_items[key] = []
191193
for nested_item in val:
192194
try:
@@ -195,21 +197,19 @@ def validate(input_dict, counter_dict):
195197
if not item.get(SKIPFAILED):
196198
raise
197199

198-
# the item is nested. we have to start over to do the same the one level deeper
199-
elif not item.get(TYPEASOARR, False):
200-
validated_items[key] = validate(input_dict[key], counter_dict[key][NESTED])
201-
202-
# the item is a "associative array" dictionary e.g keys are numerical indexes
203-
else:
204-
validation_func(key=key, val=val, **validation_args)
200+
elif type_ == TYPEASOARR:
201+
validation_func(key=key, val=val, **item)
205202

206203
for nested_key, nested_val in val.items():
207-
# make sure dictionary is present.
204+
# TODO this could be nicer
208205
if key not in validated_items:
209206
validated_items[key] = {}
210207
if nested_key not in validated_items[key]:
211208
validated_items[key][nested_key] = {}
212209

213210
validated_items[key][nested_key] = validate(nested_val, counter_dict[key][NESTED])
214211

212+
else:
213+
raise Invalid(f"type {type_} can't handle nested structures, use {TYPEASOARR}, {TYPELISTDICTS}, {TYPEDICT} instead")
214+
215215
return validated_items

maat/validations.py

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66
from .exceptions import Invalid
77

88

9-
def str_validation(val, key=None, min_length=None, max_length=None, regex=None, choices=None, cast=None):
9+
def str_validation(val, key=None, min_length=None, max_length=None, regex=None, choices=None, cast=None, *args, **kwargs):
1010
if cast:
1111
try:
1212
val = str(val)
1313
except (ValueError, TypeError):
14-
raise Invalid(f'key: "{key}" contains invalid item "{type(val).__name__}": unable to convert from type "{val}" to str')
14+
raise Invalid(f'key: "{key}" contains invalid item "{type(val).__name__}": unable to convert from type to str')
1515

1616
if not isinstance(val, str):
1717
raise Invalid(f'key: "{key}" contains invalid item "{val}" with type "{type(val).__name__}": not of type string')
@@ -31,7 +31,7 @@ def str_validation(val, key=None, min_length=None, max_length=None, regex=None,
3131
return val
3232

3333

34-
def int_validation(val, key=None, min_amount=None, max_amount=None, cast=None):
34+
def int_validation(val, key=None, min_amount=None, max_amount=None, cast=None, *args, **kwargs):
3535
if cast:
3636
try:
3737
val = int(val)
@@ -50,7 +50,7 @@ def int_validation(val, key=None, min_amount=None, max_amount=None, cast=None):
5050
return val
5151

5252

53-
def float_validation(val, key=None, min_amount=None, max_amount=None, cast=None):
53+
def float_validation(val, key=None, min_amount=None, max_amount=None, cast=None, *args, **kwargs):
5454
if cast:
5555
try:
5656
val = float(val)
@@ -69,7 +69,7 @@ def float_validation(val, key=None, min_amount=None, max_amount=None, cast=None)
6969
return val
7070

7171

72-
def list_validation(val, key=None, min_amount=None, max_amount=None):
72+
def list_validation(val, key=None, min_amount=None, max_amount=None, *args, **kwargs):
7373
if not isinstance(val, list):
7474
raise Invalid(f'key: "{key}" contains invalid item "{val}" with type "{type(val).__name__}": not of type list')
7575

@@ -82,23 +82,27 @@ def list_validation(val, key=None, min_amount=None, max_amount=None):
8282
return val
8383

8484

85-
def dict_validation(val, key=None, min_amount=None, max_amount=None, key_min=None, key_max=None, key_regex=None):
85+
def dict_validation(val, key=None, min_amount=None, max_amount=None, key_min=None, key_max=None, key_regex=None, *args, **kwargs):
8686
if not isinstance(val, dict):
8787
raise Invalid(f'"{key}": is not a dictionary')
8888

8989
if min_amount is not None and len(val) < min_amount:
90-
raise Invalid(f'key: "{key}" contains invalid item "{val}": contains less then minimal amount of {min_amount}')
90+
raise Invalid(f'key: "{key}" contains invalid item "{val}": {len(val)} contains less then minimal amount of {min_amount}')
9191

9292
if max_amount is not None and len(val) > max_amount:
93-
raise Invalid(f'key: "{key}" contains invalid item "{val}": contains more then maximum amount of {max_amount}')
93+
raise Invalid(f'key: "{key}" contains invalid item "{val}": {len(val)} contains more then maximum amount of {max_amount}')
9494

9595
if key_regex is not None and not all(bool(re.match(key_regex, str(i))) for i in val.keys()):
96-
raise Invalid(f'{key}: has dictionary key that does not adhere to regex {key_regex}')
96+
for i in val.keys():
97+
if not re.match(key_regex, str(i)):
98+
failed = str(i)
99+
break
100+
raise Invalid(f'{key}: has dictionary key "{failed}" that does not adhere to regex {key_regex}')
97101

98102
return val
99103

100104

101-
def uuid_validation(val, key=None):
105+
def uuid_validation(val, key=None, *args, **kwargs):
102106
try:
103107
_ = UUID(val, version=4)
104108
except (ValueError, AttributeError, TypeError):
@@ -107,11 +111,13 @@ def uuid_validation(val, key=None):
107111
return val
108112

109113

110-
registered_functions = {
114+
types = {
111115
'int': int_validation,
112116
'str': str_validation,
113117
'float': float_validation,
114118
'uuid': uuid_validation,
115119
'list': list_validation,
120+
'list_dicts': list_validation,
116121
'dict': dict_validation,
122+
'aso_array': dict_validation,
117123
}

maat/version.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
VERSION = "3.0.0"

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@
99
name='Maat',
1010
author='Melvin Bijman',
1111
author_email='[email protected]',
12-
version='2.0.0',
12+
version='3.0.0',
1313
license='MIT',
1414

1515
py_modules=['maat'],
1616
packages=['maat'],
1717

1818
platforms=['any'],
1919

20-
description='Validate like Maat',
20+
description='Validate like a Maat',
2121
long_description=long_description,
2222
long_description_content_type='text/markdown',
2323

0 commit comments

Comments
 (0)