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 TMaybePositiveInt = TOr(TPositiveInt, TNone)
327
328
329 TStrictPositiveInt = \
330 TAnd(TInt, WithDesc("GreaterThanZero")(lambda v: v > 0))
331
332
333 TMaybeStrictPositiveInt = TOr(TStrictPositiveInt, TNone)
334
335
336 TStrictNegativeInt = \
337 TAnd(TInt, WithDesc("LessThanZero")(compat.partial(operator.gt, 0)))
338
339
340 TPositiveFloat = \
341 TAnd(TFloat, WithDesc("EqualGreaterZero")(lambda v: v >= 0.0))
342
343
344 TJobId = WithDesc("JobId")(TOr(TPositiveInt,
345 TRegex(re.compile("^%s$" %
346 constants.JOB_ID_TEMPLATE))))
347
348
349 TNumber = TOr(TInt, TFloat)
350
351
352 TRelativeJobId = WithDesc("RelativeJobId")(TStrictNegativeInt)
356 """Checks if a given value is a list with all elements of the same type.
357
358 """
359 desc = WithDesc("List of %s" % (Parens(my_type), ))
360 return desc(TAnd(TList, lambda lst: compat.all(my_type(v) for v in lst)))
361
362
363 TMaybeListOf = lambda item_type: TOr(TNone, TListOf(item_type))
364
365
366 -def TDictOf(key_type, val_type):
367 """Checks a dict type for the type of its key/values.
368
369 """
370 desc = WithDesc("Dictionary with keys of %s and values of %s" %
371 (Parens(key_type), Parens(val_type)))
372
373 def fn(container):
374 return (compat.all(key_type(v) for v in container.keys()) and
375 compat.all(val_type(v) for v in container.values()))
376
377 return desc(TAnd(TDict, fn))
378
381 """Helper function for L{TStrictDict}.
382
383 """
384 notfound_fn = lambda _: not exclusive
385
386 if require_all and not frozenset(val.keys()).issuperset(items.keys()):
387
388 return False
389
390 return compat.all(items.get(key, notfound_fn)(value)
391 for (key, value) in val.items())
392
395 """Strict dictionary check with specific keys.
396
397 @type require_all: boolean
398 @param require_all: Whether all keys in L{items} are required
399 @type exclusive: boolean
400 @param exclusive: Whether only keys listed in L{items} should be accepted
401 @type items: dictionary
402 @param items: Mapping from key (string) to verification function
403
404 """
405 descparts = ["Dictionary containing"]
406
407 if exclusive:
408 descparts.append(" none but the")
409
410 if require_all:
411 descparts.append(" required")
412
413 if len(items) == 1:
414 descparts.append(" key ")
415 else:
416 descparts.append(" keys ")
417
418 descparts.append(utils.CommaJoin("\"%s\" (value %s)" % (key, value)
419 for (key, value) in items.items()))
420
421 desc = WithDesc("".join(descparts))
422
423 return desc(TAnd(TDict,
424 compat.partial(_TStrictDictCheck, require_all, exclusive,
425 items)))
426
429 """Checks individual items of a container.
430
431 If the verified value and the list of expected items differ in length, this
432 check considers only as many items as are contained in the shorter list. Use
433 L{TIsLength} to enforce a certain length.
434
435 @type items: list
436 @param items: List of checks
437
438 """
439 assert items, "Need items"
440
441 text = ["Item", "item"]
442 desc = WithDesc(utils.CommaJoin("%s %s is %s" %
443 (text[int(idx > 0)], idx, Parens(check))
444 for (idx, check) in enumerate(items)))
445
446 return desc(lambda value: compat.all(check(i)
447 for (check, i) in zip(items, value)))
448