1 |
1 |
#
|
2 |
2 |
#
|
3 |
3 |
|
4 |
|
# Copyright (C) 2010 Google Inc.
|
|
4 |
# Copyright (C) 2010, 2011 Google Inc.
|
5 |
5 |
#
|
6 |
6 |
# This program is free software; you can redistribute it and/or modify
|
7 |
7 |
# it under the terms of the GNU General Public License as published by
|
... | ... | |
21 |
21 |
|
22 |
22 |
"""Module implementing the parameter types code."""
|
23 |
23 |
|
|
24 |
import re
|
|
25 |
|
24 |
26 |
from ganeti import compat
|
|
27 |
from ganeti import utils
|
|
28 |
|
|
29 |
|
|
30 |
_PAREN_RE = re.compile("^[a-zA-Z0-9_-]+$")
|
|
31 |
|
|
32 |
|
|
33 |
def Parens(text):
|
|
34 |
"""Enclose text in parens if necessary.
|
|
35 |
|
|
36 |
@param text: Text
|
|
37 |
|
|
38 |
"""
|
|
39 |
text = str(text)
|
|
40 |
|
|
41 |
if _PAREN_RE.match(text):
|
|
42 |
return text
|
|
43 |
else:
|
|
44 |
return "(%s)" % text
|
|
45 |
|
|
46 |
|
|
47 |
def WithDesc(text):
|
|
48 |
"""Builds wrapper class with description text.
|
|
49 |
|
|
50 |
@type text: string
|
|
51 |
@param text: Description text
|
|
52 |
@return: Callable class
|
|
53 |
|
|
54 |
"""
|
|
55 |
assert text[0] == text[0].upper()
|
|
56 |
|
|
57 |
class wrapper(object): # pylint: disable-msg=C0103
|
|
58 |
__slots__ = ["__call__"]
|
|
59 |
|
|
60 |
def __init__(self, fn):
|
|
61 |
"""Initializes this class.
|
|
62 |
|
|
63 |
@param fn: Wrapped function
|
|
64 |
|
|
65 |
"""
|
|
66 |
self.__call__ = fn
|
|
67 |
|
|
68 |
def __str__(self):
|
|
69 |
return text
|
|
70 |
|
|
71 |
return wrapper
|
|
72 |
|
|
73 |
|
|
74 |
def CombinationDesc(op, args, fn):
|
|
75 |
"""Build description for combinating operator.
|
|
76 |
|
|
77 |
@type op: string
|
|
78 |
@param op: Operator as text (e.g. "and")
|
|
79 |
@type args: list
|
|
80 |
@param args: Operator arguments
|
|
81 |
@type fn: callable
|
|
82 |
@param fn: Wrapped function
|
|
83 |
|
|
84 |
"""
|
|
85 |
if len(args) == 1:
|
|
86 |
descr = str(args[0])
|
|
87 |
else:
|
|
88 |
descr = (" %s " % op).join(Parens(i) for i in args)
|
|
89 |
|
|
90 |
return WithDesc(descr)(fn)
|
|
91 |
|
25 |
92 |
|
26 |
93 |
# Modifiable default values; need to define these here before the
|
27 |
94 |
# actual LUs
|
28 |
95 |
|
|
96 |
@WithDesc(str([]))
|
29 |
97 |
def EmptyList():
|
30 |
98 |
"""Returns an empty list.
|
31 |
99 |
|
... | ... | |
33 |
101 |
return []
|
34 |
102 |
|
35 |
103 |
|
|
104 |
@WithDesc(str({}))
|
36 |
105 |
def EmptyDict():
|
37 |
106 |
"""Returns an empty dict.
|
38 |
107 |
|
... | ... | |
44 |
113 |
NoDefault = object()
|
45 |
114 |
|
46 |
115 |
|
47 |
|
#: The no-type (value to complex to check it in the type system)
|
|
116 |
#: The no-type (value too complex to check it in the type system)
|
48 |
117 |
NoType = object()
|
49 |
118 |
|
50 |
119 |
|
51 |
120 |
# Some basic types
|
|
121 |
@WithDesc("NotNone")
|
52 |
122 |
def TNotNone(val):
|
53 |
123 |
"""Checks if the given value is not None.
|
54 |
124 |
|
... | ... | |
56 |
126 |
return val is not None
|
57 |
127 |
|
58 |
128 |
|
|
129 |
@WithDesc("None")
|
59 |
130 |
def TNone(val):
|
60 |
131 |
"""Checks if the given value is None.
|
61 |
132 |
|
... | ... | |
63 |
134 |
return val is None
|
64 |
135 |
|
65 |
136 |
|
|
137 |
@WithDesc("Boolean")
|
66 |
138 |
def TBool(val):
|
67 |
139 |
"""Checks if the given value is a boolean.
|
68 |
140 |
|
... | ... | |
70 |
142 |
return isinstance(val, bool)
|
71 |
143 |
|
72 |
144 |
|
|
145 |
@WithDesc("Integer")
|
73 |
146 |
def TInt(val):
|
74 |
147 |
"""Checks if the given value is an integer.
|
75 |
148 |
|
... | ... | |
82 |
155 |
return isinstance(val, int) and not isinstance(val, bool)
|
83 |
156 |
|
84 |
157 |
|
|
158 |
@WithDesc("Float")
|
85 |
159 |
def TFloat(val):
|
86 |
160 |
"""Checks if the given value is a float.
|
87 |
161 |
|
... | ... | |
89 |
163 |
return isinstance(val, float)
|
90 |
164 |
|
91 |
165 |
|
|
166 |
@WithDesc("String")
|
92 |
167 |
def TString(val):
|
93 |
168 |
"""Checks if the given value is a string.
|
94 |
169 |
|
... | ... | |
96 |
171 |
return isinstance(val, basestring)
|
97 |
172 |
|
98 |
173 |
|
|
174 |
@WithDesc("EvalToTrue")
|
99 |
175 |
def TTrue(val):
|
100 |
176 |
"""Checks if a given value evaluates to a boolean True value.
|
101 |
177 |
|
... | ... | |
107 |
183 |
"""Builds a function that checks if a given value is a member of a list.
|
108 |
184 |
|
109 |
185 |
"""
|
110 |
|
return lambda val: val in target_list
|
|
186 |
def fn(val):
|
|
187 |
return val in target_list
|
|
188 |
|
|
189 |
return WithDesc("OneOf %s" % (utils.CommaJoin(target_list), ))(fn)
|
111 |
190 |
|
112 |
191 |
|
113 |
192 |
# Container types
|
|
193 |
@WithDesc("List")
|
114 |
194 |
def TList(val):
|
115 |
195 |
"""Checks if the given value is a list.
|
116 |
196 |
|
... | ... | |
118 |
198 |
return isinstance(val, list)
|
119 |
199 |
|
120 |
200 |
|
|
201 |
@WithDesc("Dictionary")
|
121 |
202 |
def TDict(val):
|
122 |
203 |
"""Checks if the given value is a dictionary.
|
123 |
204 |
|
... | ... | |
129 |
210 |
"""Check is the given container is of the given size.
|
130 |
211 |
|
131 |
212 |
"""
|
132 |
|
return lambda container: len(container) == size
|
|
213 |
def fn(container):
|
|
214 |
return len(container) == size
|
|
215 |
|
|
216 |
return WithDesc("Length %s" % (size, ))(fn)
|
133 |
217 |
|
134 |
218 |
|
135 |
219 |
# Combinator types
|
... | ... | |
139 |
223 |
"""
|
140 |
224 |
def fn(val):
|
141 |
225 |
return compat.all(t(val) for t in args)
|
142 |
|
return fn
|
|
226 |
|
|
227 |
return CombinationDesc("and", args, fn)
|
143 |
228 |
|
144 |
229 |
|
145 |
230 |
def TOr(*args):
|
... | ... | |
148 |
233 |
"""
|
149 |
234 |
def fn(val):
|
150 |
235 |
return compat.any(t(val) for t in args)
|
151 |
|
return fn
|
|
236 |
|
|
237 |
return CombinationDesc("or", args, fn)
|
152 |
238 |
|
153 |
239 |
|
154 |
240 |
def TMap(fn, test):
|
155 |
241 |
"""Checks that a modified version of the argument passes the given test.
|
156 |
242 |
|
157 |
243 |
"""
|
158 |
|
return lambda val: test(fn(val))
|
|
244 |
return WithDesc("Result of %s must be %s" %
|
|
245 |
(Parens(fn), Parens(test)))(lambda val: test(fn(val)))
|
159 |
246 |
|
160 |
247 |
|
161 |
248 |
# Type aliases
|
162 |
249 |
|
163 |
250 |
#: a non-empty string
|
164 |
|
TNonEmptyString = TAnd(TString, TTrue)
|
|
251 |
TNonEmptyString = WithDesc("NonEmptyString")(TAnd(TString, TTrue))
|
165 |
252 |
|
166 |
253 |
#: a maybe non-empty string
|
167 |
254 |
TMaybeString = TOr(TNonEmptyString, TNone)
|
... | ... | |
173 |
260 |
TMaybeDict = TOr(TDict, TNone)
|
174 |
261 |
|
175 |
262 |
#: a positive integer
|
176 |
|
TPositiveInt = TAnd(TInt, lambda v: v >= 0)
|
|
263 |
TPositiveInt = \
|
|
264 |
TAnd(TInt, WithDesc("EqualGreaterZero")(lambda v: v >= 0))
|
177 |
265 |
|
178 |
266 |
#: a strictly positive integer
|
179 |
|
TStrictPositiveInt = TAnd(TInt, lambda v: v > 0)
|
|
267 |
TStrictPositiveInt = \
|
|
268 |
TAnd(TInt, WithDesc("GreaterThanZero")(lambda v: v > 0))
|
180 |
269 |
|
181 |
270 |
|
182 |
271 |
def TListOf(my_type):
|
183 |
272 |
"""Checks if a given value is a list with all elements of the same type.
|
184 |
273 |
|
185 |
274 |
"""
|
186 |
|
return TAnd(TList,
|
187 |
|
lambda lst: compat.all(my_type(v) for v in lst))
|
|
275 |
desc = WithDesc("List of %s" % (Parens(my_type), ))
|
|
276 |
return desc(TAnd(TList, lambda lst: compat.all(my_type(v) for v in lst)))
|
188 |
277 |
|
189 |
278 |
|
190 |
279 |
def TDictOf(key_type, val_type):
|
191 |
280 |
"""Checks a dict type for the type of its key/values.
|
192 |
281 |
|
193 |
282 |
"""
|
194 |
|
return TAnd(TDict,
|
195 |
|
lambda my_dict: (compat.all(key_type(v) for v in my_dict.keys())
|
196 |
|
and compat.all(val_type(v)
|
197 |
|
for v in my_dict.values())))
|
|
283 |
desc = WithDesc("Dictionary with keys of %s and values of %s" %
|
|
284 |
(Parens(key_type), Parens(val_type)))
|
|
285 |
|
|
286 |
def fn(container):
|
|
287 |
return (compat.all(key_type(v) for v in container.keys()) and
|
|
288 |
compat.all(val_type(v) for v in container.values()))
|
|
289 |
|
|
290 |
return desc(TAnd(TDict, fn))
|