-
-
Notifications
You must be signed in to change notification settings - Fork 53
/
validation.py
153 lines (114 loc) · 4.75 KB
/
validation.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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
"""Provide decorators for validating parameters."""
from decorator import decorator
from plone.api.exc import InvalidParameterError
from plone.api.exc import MissingParameterError
import inspect
def _get_arg_spec(func, validator_args):
"""Get the arguments specified in the function spec.
and check that the decorator doesn't refer to non-existent args.
"""
signature_args = inspect.getfullargspec(func).args
extra_args = set(validator_args) - set(signature_args)
if extra_args:
raise ValueError(
"Validator for {name} refers to parameters "
"that are not part of the function signature: {signature}".format(
name=func.__name__,
signature=", ".join(extra_args),
),
)
return signature_args
def _get_supplied_args(signature_params, args, kwargs):
"""Return names of all args that have been passed in.
either as positional or keyword arguments, and are not None.
"""
supplied_args = []
for index in range(len(args)):
if args[index] is not None:
supplied_args.append(signature_params[index])
for keyword in kwargs:
if kwargs[keyword] is not None:
supplied_args.append(keyword)
return supplied_args
def required_parameters(*required_params):
"""Test whether all of the specified parameters have been supplied and are not None.
Todo: add an optional flag to allow None values through as valid parameters
Usage:
@required_parameters('a', 'b')
def foo(a=None, b=None, c=None):
pass
"""
def _required_parameters(func):
"""Provide actual decorator."""
signature_params = _get_arg_spec(func, required_params)
def wrapped(function, *args, **kwargs):
"""Provide wrapped function (whose docstring will get replaced)."""
supplied_args = _get_supplied_args(signature_params, args, kwargs)
missing = [param for param in required_params if param not in supplied_args]
if len(missing):
raise MissingParameterError(
"Missing required parameter(s): {params}".format(
params=", ".join(missing),
),
)
return function(*args, **kwargs)
return decorator(wrapped, func)
return _required_parameters
def mutually_exclusive_parameters(*exclusive_params):
"""Provide decorator.
The decorator raises an exception if more than one
of the specified parameters has been supplied and is not None
Usage:
@mutually_exclusive_parameters('a', 'b')
def foo(a=None, b=None, c=None):
pass
"""
def _mutually_exclusive_parameters(func):
"""Provide a decorator."""
signature_params = _get_arg_spec(func, exclusive_params)
def wrapped(function, *args, **kwargs):
"""Provide a wrapped function (whose docstring will get replaced)."""
supplied_args = _get_supplied_args(signature_params, args, kwargs)
clashes = [
argument for argument in supplied_args if argument in exclusive_params
]
if len(clashes) > 1:
raise InvalidParameterError(
"These parameters are mutually exclusive: {arg}.".format(
arg=", ".join(clashes),
),
)
return function(*args, **kwargs)
return decorator(wrapped, func)
return _mutually_exclusive_parameters
def at_least_one_of(*candidate_params):
"""Provide a decorator.
The decorator raises an exception if none of the
specified parameters has been supplied. Can be used in conjunction with
mutually_exclusive_parameters to enforce exactly one.
Usage:
@at_least_one_of('a', 'b')
def foo(a=None, b=None, c=None):
pass
"""
def _at_least_one_of(func):
"""Provide an actual decorator."""
signature_params = _get_arg_spec(func, candidate_params)
def wrapped(function, *args, **kwargs):
"""Provide a wrapped function (whose docstring will get replaced)."""
supplied_args = _get_supplied_args(signature_params, args, kwargs)
candidates = [
candidate
for candidate in supplied_args
if candidate in candidate_params
]
if len(candidates) < 1:
raise MissingParameterError(
"At least one of these parameters must be "
"supplied: {params}.".format(
params=", ".join(candidate_params),
),
)
return function(*args, **kwargs)
return decorator(wrapped, func)
return _at_least_one_of