Revision 8c9ee749 lib/ht.py
b/lib/ht.py | ||
---|---|---|
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)) |
Also available in: Unified diff