1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """Module implementing the parameter types code."""
23
24 import re
25 import operator
26
27 from ganeti import compat
28 from ganeti import utils
29 from ganeti import constants
30
31
32 _PAREN_RE = re.compile("^[a-zA-Z0-9_-]+$")
36 """Enclose text in parens if necessary.
37
38 @param text: Text
39
40 """
41 text = str(text)
42
43 if _PAREN_RE.match(text):
44 return text
45 else:
46 return "(%s)" % text
47
50 __slots__ = [
51 "_fn",
52 "_text",
53 ]
54
56 """Initializes this class.
57
58 @param text: Description
59 @param fn: Wrapped function
60
61 """
62 assert text.strip()
63
64 self._text = text
65 self._fn = fn
66
68 return self._fn(*args)
69
72 """Wrapper class for description text.
73
74 """
77
85
88 """Builds wrapper class with description text.
89
90 @type text: string
91 @param text: Description text
92 @return: Callable class
93
94 """
95 assert text[0] == text[0].upper()
96
97 return compat.partial(_DescWrapper, text)
98
111
114 """Build description for combinating operator.
115
116 @type op: string
117 @param op: Operator as text (e.g. "and")
118 @type args: list
119 @param args: Operator arguments
120 @type fn: callable
121 @param fn: Wrapped function
122
123 """
124 if len(args) == 1:
125 descr = str(args[0])
126 else:
127 descr = (" %s " % op).join(Parens(i) for i in args)
128
129 return WithDesc(descr)(fn)
130
131
132
133
134
135 @WithDesc(str([]))
136 -def EmptyList():
137 """Returns an empty list.
138
139 """
140 return []
141
145 """Returns an empty dict.
146
147 """
148 return {}
149
150
151
152 NoDefault = object()
153
154
155
156 NoType = object()
157
158
159
160 @WithDesc("Anything")
161 -def TAny(_):
162 """Accepts any value.
163
164 """
165 return True
166
167
168 @WithDesc("NotNone")
169 -def TNotNone(val):
170 """Checks if the given value is not None.
171
172 """
173 return val is not None
174
175
176 @WithDesc("None")
177 -def TNone(val):
178 """Checks if the given value is None.
179
180 """
181 return val is None
182
183
184 @WithDesc("Boolean")
185 -def TBool(val):
186 """Checks if the given value is a boolean.
187
188 """
189 return isinstance(val, bool)
190
191
192 @WithDesc("Integer")
193 -def TInt(val):
194 """Checks if the given value is an integer.
195
196 """
197
198
199
200
201
202 return isinstance(val, (int, long)) and not isinstance(val, bool)
203
204
205 @WithDesc("Float")
206 -def TFloat(val):
207 """Checks if the given value is a float.
208
209 """
210 return isinstance(val, float)
211
212
213 @WithDesc("String")
214 -def TString(val):
215 """Checks if the given value is a string.
216
217 """
218 return isinstance(val, basestring)
219
220
221 @WithDesc("EvalToTrue")
222 -def TTrue(val):
223 """Checks if a given value evaluates to a boolean True value.
224
225 """
226 return bool(val)
227
230 """Builds a function that checks if a given value is a member of a list.
231
232 """
233 def fn(val):
234 return val in target_list
235
236 return WithDesc("OneOf %s" % (utils.CommaJoin(target_list), ))(fn)
237
238
239
240 @WithDesc("List")
241 -def TList(val):
242 """Checks if the given value is a list.
243
244 """
245 return isinstance(val, list)
246
247
248 @WithDesc("Dictionary")
249 -def TDict(val):
250 """Checks if the given value is a dictionary.
251
252 """
253 return isinstance(val, dict)
254
257 """Check is the given container is of the given size.
258
259 """
260 def fn(container):
261 return len(container) == size
262
263 return WithDesc("Length %s" % (size, ))(fn)
264
265
266
267 -def TAnd(*args):
268 """Combine multiple functions using an AND operation.
269
270 """
271 def fn(val):
272 return compat.all(t(val) for t in args)
273
274 return CombinationDesc("and", args, fn)
275
278 """Combine multiple functions using an AND operation.
279
280 """
281 def fn(val):
282 return compat.any(t(val) for t in args)
283
284 return CombinationDesc("or", args, fn)
285
286
287 -def TMap(fn, test):
288 """Checks that a modified version of the argument passes the given test.
289
290 """
291 return WithDesc("Result of %s must be %s" %
292 (Parens(fn), Parens(test)))(lambda val: test(fn(val)))
293
296 """Checks whether a string matches a specific regular expression.
297
298 @param pobj: Compiled regular expression as returned by C{re.compile}
299
300 """
301 desc = WithDesc("String matching regex \"%s\"" %
302 pobj.pattern.encode("string_escape"))
303
304 return desc(TAnd(TString, pobj.match))
305
306
307
308
309
310 TNonEmptyString = WithDesc("NonEmptyString")(TAnd(TString, TTrue))
311
312
313 TMaybeString = TOr(TNonEmptyString, TNone)
314
315
316 TMaybeBool = TOr(TBool, TNone)
317
318
319 TMaybeDict = TOr(TDict, TNone)
320
321
322 TPositiveInt = \
323 TAnd(TInt, WithDesc("EqualGreaterZero")(lambda v: v >= 0))
324
325
326 TStrictPositiveInt = \
327 TAnd(TInt, WithDesc("GreaterThanZero")(lambda v: v > 0))
328
329
330 TStrictNegativeInt = \
331 TAnd(TInt, WithDesc("LessThanZero")(compat.partial(operator.gt, 0)))
332
333
334 TPositiveFloat = \
335 TAnd(TFloat, WithDesc("EqualGreaterZero")(lambda v: v >= 0.0))
336
337
338 TJobId = WithDesc("JobId")(TOr(TPositiveInt,
339 TRegex(re.compile("^%s$" %
340 constants.JOB_ID_TEMPLATE))))
341
342
343 TNumber = TOr(TInt, TFloat)
344
345
346 TRelativeJobId = WithDesc("RelativeJobId")(TStrictNegativeInt)
350 """Checks if a given value is a list with all elements of the same type.
351
352 """
353 desc = WithDesc("List of %s" % (Parens(my_type), ))
354 return desc(TAnd(TList, lambda lst: compat.all(my_type(v) for v in lst)))
355
356
357 -def TDictOf(key_type, val_type):
358 """Checks a dict type for the type of its key/values.
359
360 """
361 desc = WithDesc("Dictionary with keys of %s and values of %s" %
362 (Parens(key_type), Parens(val_type)))
363
364 def fn(container):
365 return (compat.all(key_type(v) for v in container.keys()) and
366 compat.all(val_type(v) for v in container.values()))
367
368 return desc(TAnd(TDict, fn))
369
372 """Helper function for L{TStrictDict}.
373
374 """
375 notfound_fn = lambda _: not exclusive
376
377 if require_all and not frozenset(val.keys()).issuperset(items.keys()):
378
379 return False
380
381 return compat.all(items.get(key, notfound_fn)(value)
382 for (key, value) in val.items())
383
386 """Strict dictionary check with specific keys.
387
388 @type require_all: boolean
389 @param require_all: Whether all keys in L{items} are required
390 @type exclusive: boolean
391 @param exclusive: Whether only keys listed in L{items} should be accepted
392 @type items: dictionary
393 @param items: Mapping from key (string) to verification function
394
395 """
396 descparts = ["Dictionary containing"]
397
398 if exclusive:
399 descparts.append(" none but the")
400
401 if require_all:
402 descparts.append(" required")
403
404 if len(items) == 1:
405 descparts.append(" key ")
406 else:
407 descparts.append(" keys ")
408
409 descparts.append(utils.CommaJoin("\"%s\" (value %s)" % (key, value)
410 for (key, value) in items.items()))
411
412 desc = WithDesc("".join(descparts))
413
414 return desc(TAnd(TDict,
415 compat.partial(_TStrictDictCheck, require_all, exclusive,
416 items)))
417
420 """Checks individual items of a container.
421
422 If the verified value and the list of expected items differ in length, this
423 check considers only as many items as are contained in the shorter list. Use
424 L{TIsLength} to enforce a certain length.
425
426 @type items: list
427 @param items: List of checks
428
429 """
430 assert items, "Need items"
431
432 text = ["Item", "item"]
433 desc = WithDesc(utils.CommaJoin("%s %s is %s" %
434 (text[int(idx > 0)], idx, Parens(check))
435 for (idx, check) in enumerate(items)))
436
437 return desc(lambda value: compat.all(check(i)
438 for (check, i) in zip(items, value)))
439