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
125
126
127 if __debug__ and TNone in args and args.index(TNone) > 0:
128 raise Exception("TNone must be listed first")
129
130 if len(args) == 1:
131 descr = str(args[0])
132 else:
133 descr = (" %s " % op).join(Parens(i) for i in args)
134
135 return WithDesc(descr)(fn)
136
137
138
139
140
141 @WithDesc(str([]))
142 -def EmptyList():
143 """Returns an empty list.
144
145 """
146 return []
147
151 """Returns an empty dict.
152
153 """
154 return {}
155
156
157
158 NoDefault = object()
159
160
161
162 NoType = object()
163
164
165
166 @WithDesc("Anything")
167 -def TAny(_):
168 """Accepts any value.
169
170 """
171 return True
172
173
174 @WithDesc("NotNone")
175 -def TNotNone(val):
176 """Checks if the given value is not None.
177
178 """
179 return val is not None
180
181
182 @WithDesc("None")
183 -def TNone(val):
184 """Checks if the given value is None.
185
186 """
187 return val is None
188
189
190 @WithDesc("ValueNone")
191 -def TValueNone(val):
192 """Checks if the given value is L{constants.VALUE_NONE}.
193
194 """
195 return val == constants.VALUE_NONE
196
197
198 @WithDesc("Boolean")
199 -def TBool(val):
200 """Checks if the given value is a boolean.
201
202 """
203 return isinstance(val, bool)
204
205
206 @WithDesc("Integer")
207 -def TInt(val):
208 """Checks if the given value is an integer.
209
210 """
211
212
213
214
215
216 return isinstance(val, (int, long)) and not isinstance(val, bool)
217
218
219 @WithDesc("Float")
220 -def TFloat(val):
221 """Checks if the given value is a float.
222
223 """
224 return isinstance(val, float)
225
226
227 @WithDesc("String")
228 -def TString(val):
229 """Checks if the given value is a string.
230
231 """
232 return isinstance(val, basestring)
233
234
235 @WithDesc("EvalToTrue")
236 -def TTrue(val):
237 """Checks if a given value evaluates to a boolean True value.
238
239 """
240 return bool(val)
241
244 """Builds a function that checks if a given value is a member of a list.
245
246 """
247 def fn(val):
248 return val in target_list
249
250 return WithDesc("OneOf %s" % (utils.CommaJoin(target_list), ))(fn)
251
252
253
254 @WithDesc("List")
255 -def TList(val):
256 """Checks if the given value is a list.
257
258 """
259 return isinstance(val, list)
260
261
262 @WithDesc("Tuple")
263 -def TTuple(val):
264 """Checks if the given value is a tuple.
265
266 """
267 return isinstance(val, tuple)
268
269
270 @WithDesc("Dictionary")
271 -def TDict(val):
272 """Checks if the given value is a dictionary.
273
274 """
275 return isinstance(val, dict)
276
279 """Check is the given container is of the given size.
280
281 """
282 def fn(container):
283 return len(container) == size
284
285 return WithDesc("Length %s" % (size, ))(fn)
286
287
288
289 -def TAnd(*args):
290 """Combine multiple functions using an AND operation.
291
292 """
293 def fn(val):
294 return compat.all(t(val) for t in args)
295
296 return CombinationDesc("and", args, fn)
297
300 """Combine multiple functions using an AND operation.
301
302 """
303 def fn(val):
304 return compat.any(t(val) for t in args)
305
306 return CombinationDesc("or", args, fn)
307
308
309 -def TMap(fn, test):
310 """Checks that a modified version of the argument passes the given test.
311
312 """
313 return WithDesc("Result of %s must be %s" %
314 (Parens(fn), Parens(test)))(lambda val: test(fn(val)))
315
318 """Checks whether a string matches a specific regular expression.
319
320 @param pobj: Compiled regular expression as returned by C{re.compile}
321
322 """
323 desc = WithDesc("String matching regex \"%s\"" %
324 pobj.pattern.encode("string_escape"))
325
326 return desc(TAnd(TString, pobj.match))
327
330 """Wrap a test in a TOr(TNone, test).
331
332 This makes it easier to define TMaybe* types.
333
334 """
335 return TOr(TNone, test)
336
343
344
345
346
347
348 TNonEmptyString = WithDesc("NonEmptyString")(TAnd(TString, TTrue))
349
350
351 TMaybeString = TMaybe(TNonEmptyString)
352
353
354 TMaybeBool = TMaybe(TBool)
355
356
357 TMaybeDict = TMaybe(TDict)
358
359
360 TNonNegativeInt = \
361 TAnd(TInt, WithDesc("EqualOrGreaterThanZero")(lambda v: v >= 0))
362
363
364 TPositiveInt = \
365 TAnd(TInt, WithDesc("GreaterThanZero")(lambda v: v > 0))
366
367
368 TMaybePositiveInt = TMaybe(TPositiveInt)
369
370
371 TNegativeInt = \
372 TAnd(TInt, WithDesc("LessThanZero")(compat.partial(operator.gt, 0)))
373
374
375 TNonNegativeFloat = \
376 TAnd(TFloat, WithDesc("EqualOrGreaterThanZero")(lambda v: v >= 0.0))
377
378
379 TJobId = WithDesc("JobId")(TOr(TNonNegativeInt,
380 TRegex(re.compile("^%s$" %
381 constants.JOB_ID_TEMPLATE))))
382
383
384 TNumber = TOr(TInt, TFloat)
385
386
387 TRelativeJobId = WithDesc("RelativeJobId")(TNegativeInt)
391 """Checks if a given value is an instance of C{cls}.
392
393 @type cls: class
394 @param cls: Class object
395
396 """
397 name = "%s.%s" % (cls.__module__, cls.__name__)
398
399 desc = WithDesc("Instance of %s" % (Parens(name), ))
400
401 return desc(lambda val: isinstance(val, cls))
402
405 """Checks if a given value is a list with all elements of the same type.
406
407 """
408 desc = WithDesc("List of %s" % (Parens(my_type), ))
409 return desc(TAnd(TList, lambda lst: compat.all(my_type(v) for v in lst)))
410
411
412 TMaybeListOf = lambda item_type: TMaybe(TListOf(item_type))
413
414
415 -def TDictOf(key_type, val_type):
416 """Checks a dict type for the type of its key/values.
417
418 """
419 desc = WithDesc("Dictionary with keys of %s and values of %s" %
420 (Parens(key_type), Parens(val_type)))
421
422 def fn(container):
423 return (compat.all(key_type(v) for v in container.keys()) and
424 compat.all(val_type(v) for v in container.values()))
425
426 return desc(TAnd(TDict, fn))
427
430 """Helper function for L{TStrictDict}.
431
432 """
433 notfound_fn = lambda _: not exclusive
434
435 if require_all and not frozenset(val.keys()).issuperset(items.keys()):
436
437 return False
438
439 return compat.all(items.get(key, notfound_fn)(value)
440 for (key, value) in val.items())
441
444 """Strict dictionary check with specific keys.
445
446 @type require_all: boolean
447 @param require_all: Whether all keys in L{items} are required
448 @type exclusive: boolean
449 @param exclusive: Whether only keys listed in L{items} should be accepted
450 @type items: dictionary
451 @param items: Mapping from key (string) to verification function
452
453 """
454 descparts = ["Dictionary containing"]
455
456 if exclusive:
457 descparts.append(" none but the")
458
459 if require_all:
460 descparts.append(" required")
461
462 if len(items) == 1:
463 descparts.append(" key ")
464 else:
465 descparts.append(" keys ")
466
467 descparts.append(utils.CommaJoin("\"%s\" (value %s)" % (key, value)
468 for (key, value) in items.items()))
469
470 desc = WithDesc("".join(descparts))
471
472 return desc(TAnd(TDict,
473 compat.partial(_TStrictDictCheck, require_all, exclusive,
474 items)))
475
478 """Checks individual items of a container.
479
480 If the verified value and the list of expected items differ in length, this
481 check considers only as many items as are contained in the shorter list. Use
482 L{TIsLength} to enforce a certain length.
483
484 @type items: list
485 @param items: List of checks
486
487 """
488 assert items, "Need items"
489
490 text = ["Item", "item"]
491 desc = WithDesc(utils.CommaJoin("%s %s is %s" %
492 (text[int(idx > 0)], idx, Parens(check))
493 for (idx, check) in enumerate(items)))
494
495 return desc(lambda value: compat.all(check(i)
496 for (check, i) in zip(items, value)))
497