-
Notifications
You must be signed in to change notification settings - Fork 0
/
vf.py
executable file
·120 lines (100 loc) · 3.96 KB
/
vf.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
"""
VF: Validation Functions (for Python dicts.)
Copyright (c) 2020 Polydojo, Inc.
SOFTWARE LICENSING
------------------
The software is released "AS IS" under the MIT License,
WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. Kindly
see LICENSE.txt for more details.
NO TRADEMARK RIGHTS
-------------------
The above software licensing terms DO NOT grant any right in the
trademarks, service marks, brand names or logos of Polydojo, Inc.
""";
import functools;
import re;
__version__ = "0.0.2"; # Req'd by flit.
############################################################
# SIMPLE: ##################################################
############################################################
identity = lambda x: x;
truthy = lambda x: bool(x);
falsy = lambda x: not x;
noneIs = lambda x: x is None;
############################################################
# CHECKER MAKERS: ##########################################
############################################################
def typeIs (typ): # <-- TODO: Intro truthy option.
"Makes `func (x)` for checking `type(x) is typ`.";
return lambda x: type(x) is typ;
def instanceOf (*typs):
"Makes `func (x)` for checking `isinstance(x, typs)`.";
return lambda x: isinstance(x, typs);
def typeIn (*typs):
"Makes `func (x)` for checking `type(x) in typs`.";
return lambda x: type(x) in typs;
def patternIs (pattern):
"Makes `func (s)` for checking `s` against `pattern`.";
if type(pattern) is str:
return lambda s: bool(re.match(pattern, s));
if type(pattern) is re.Pattern:
return lambda s: bool(pattern.match(s));
raise ValueError("Expected `pattern` to be of type "
"`str` or `re.Pattern`, not: %r" % (pattern,)
);
def allOf (*fns):
"Makes `func (x)` for checking `all(fn(x) for fn in fns)`.";
return lambda x: all(map(lambda fn: fn(x), fns));
def anyOf (*fns):
"Makes `func (x)` for checking `any(fn(x) for fn in fns)`.";
return lambda x: any(map(lambda fn: fn(x), fns));
def listOf (fn, minLen=0):
"Makes `func (li)` for checking `all(fn(x) for x in li)`.";
return lambda li: (
isinstance(li, list) and
len(li) >= minLen and
all(map(fn, li)) #and
);
############################################################
# DICT VALIDATION: #########################################
############################################################
class BadSchemaError (ValueError): pass;
class ValidationError (ValueError): pass;
def _validateSchemaItself (schema):
"Ensures that `schema` itself is valid.";
if not isinstance(schema, dict):
raise BadSchemaError("Not an instance of `dict`.");
for key, rhsFn in schema.items():
if not callable(rhsFn):
raise BadSchemaError(
"Non-callable value against key: %r" % (key,),
);
return True;
def dictOf (schema, extraKeysOk=False):
"Makes `func (d)` for VALIDATING `d` against `schema`.";
assert _validateSchemaItself(schema);
def validateFn (d):
if not isinstance(d, dict):
raise ValidationError(
"Expected dict-like object, not: %r" % (d,),
);
dKeySet = set(d.keys());
sKeySet = set(schema.keys());
if not dKeySet.issuperset(sKeySet):
raise ValidationError("Dict-like object is missing " +
"required keys: {}".format(sKeySet - dKeySet) #+
);
if (not extraKeysOk) and (dKeySet != sKeySet):
raise ValidationError("Dict-like object has " +
"excess keys: {}".format(dKeySet - sKeySet) #+
);
for key, rhsFn in schema.items():
assert callable(rhsFn);
if not rhsFn(d[key]):
raise ValidationError(
("Against key: %r\n" % (key,)) +
("Unexpected value: %r" % (d[key],)) #+
);
return True;
return validateFn;
# End ######################################################