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 OR 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 TMaybeList = TMaybe(TList)
361
362
363 TNonNegativeInt = \
364 TAnd(TInt, WithDesc("EqualOrGreaterThanZero")(lambda v: v >= 0))
365
366
367 TPositiveInt = \
368 TAnd(TInt, WithDesc("GreaterThanZero")(lambda v: v > 0))
369
370
371 TMaybePositiveInt = TMaybe(TPositiveInt)
372
373
374 TNegativeInt = \
375 TAnd(TInt, WithDesc("LessThanZero")(compat.partial(operator.gt, 0)))
376
377
378 TNonNegativeFloat = \
379 TAnd(TFloat, WithDesc("EqualOrGreaterThanZero")(lambda v: v >= 0.0))
380
381
382 TJobId = WithDesc("JobId")(TOr(TNonNegativeInt,
383 TRegex(re.compile("^%s$" %
384 constants.JOB_ID_TEMPLATE))))
385
386
387 TNumber = TOr(TInt, TFloat)
388
389
390 TRelativeJobId = WithDesc("RelativeJobId")(TNegativeInt)
394 """Checks if a given value is an instance of C{cls}.
395
396 @type cls: class
397 @param cls: Class object
398
399 """
400 name = "%s.%s" % (cls.__module__, cls.__name__)
401
402 desc = WithDesc("Instance of %s" % (Parens(name), ))
403
404 return desc(lambda val: isinstance(val, cls))
405
408 """Checks if a given value is a list with all elements of the same type.
409
410 """
411 desc = WithDesc("List of %s" % (Parens(my_type), ))
412 return desc(TAnd(TList, lambda lst: compat.all(my_type(v) for v in lst)))
413
414
415 TMaybeListOf = lambda item_type: TMaybe(TListOf(item_type))
416
417
418 -def TDictOf(key_type, val_type):
419 """Checks a dict type for the type of its key/values.
420
421 """
422 desc = WithDesc("Dictionary with keys of %s and values of %s" %
423 (Parens(key_type), Parens(val_type)))
424
425 def fn(container):
426 return (compat.all(key_type(v) for v in container.keys()) and
427 compat.all(val_type(v) for v in container.values()))
428
429 return desc(TAnd(TDict, fn))
430
433 """Helper function for L{TStrictDict}.
434
435 """
436 notfound_fn = lambda _: not exclusive
437
438 if require_all and not frozenset(val.keys()).issuperset(items.keys()):
439
440 return False
441
442 return compat.all(items.get(key, notfound_fn)(value)
443 for (key, value) in val.items())
444
447 """Strict dictionary check with specific keys.
448
449 @type require_all: boolean
450 @param require_all: Whether all keys in L{items} are required
451 @type exclusive: boolean
452 @param exclusive: Whether only keys listed in L{items} should be accepted
453 @type items: dictionary
454 @param items: Mapping from key (string) to verification function
455
456 """
457 descparts = ["Dictionary containing"]
458
459 if exclusive:
460 descparts.append(" none but the")
461
462 if require_all:
463 descparts.append(" required")
464
465 if len(items) == 1:
466 descparts.append(" key ")
467 else:
468 descparts.append(" keys ")
469
470 descparts.append(utils.CommaJoin("\"%s\" (value %s)" % (key, value)
471 for (key, value) in items.items()))
472
473 desc = WithDesc("".join(descparts))
474
475 return desc(TAnd(TDict,
476 compat.partial(_TStrictDictCheck, require_all, exclusive,
477 items)))
478
481 """Checks individual items of a container.
482
483 If the verified value and the list of expected items differ in length, this
484 check considers only as many items as are contained in the shorter list. Use
485 L{TIsLength} to enforce a certain length.
486
487 @type items: list
488 @param items: List of checks
489
490 """
491 assert items, "Need items"
492
493 text = ["Item", "item"]
494 desc = WithDesc(utils.CommaJoin("%s %s is %s" %
495 (text[int(idx > 0)], idx, Parens(check))
496 for (idx, check) in enumerate(items)))
497
498 return desc(lambda value: compat.all(check(i)
499 for (check, i) in zip(items, value)))
500