root / lib / ht.py @ b99b607f
History | View | Annotate | Download (8.4 kB)
1 | 62e0e880 | Iustin Pop | #
|
---|---|---|---|
2 | 62e0e880 | Iustin Pop | #
|
3 | 62e0e880 | Iustin Pop | |
4 | 8c9ee749 | Michael Hanselmann | # Copyright (C) 2010, 2011 Google Inc.
|
5 | 62e0e880 | Iustin Pop | #
|
6 | 62e0e880 | Iustin Pop | # This program is free software; you can redistribute it and/or modify
|
7 | 62e0e880 | Iustin Pop | # it under the terms of the GNU General Public License as published by
|
8 | 62e0e880 | Iustin Pop | # the Free Software Foundation; either version 2 of the License, or
|
9 | 62e0e880 | Iustin Pop | # (at your option) any later version.
|
10 | 62e0e880 | Iustin Pop | #
|
11 | 62e0e880 | Iustin Pop | # This program is distributed in the hope that it will be useful, but
|
12 | 62e0e880 | Iustin Pop | # WITHOUT ANY WARRANTY; without even the implied warranty of
|
13 | 62e0e880 | Iustin Pop | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14 | 62e0e880 | Iustin Pop | # General Public License for more details.
|
15 | 62e0e880 | Iustin Pop | #
|
16 | 62e0e880 | Iustin Pop | # You should have received a copy of the GNU General Public License
|
17 | 62e0e880 | Iustin Pop | # along with this program; if not, write to the Free Software
|
18 | 62e0e880 | Iustin Pop | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
19 | 62e0e880 | Iustin Pop | # 02110-1301, USA.
|
20 | 62e0e880 | Iustin Pop | |
21 | 62e0e880 | Iustin Pop | |
22 | 62e0e880 | Iustin Pop | """Module implementing the parameter types code."""
|
23 | 62e0e880 | Iustin Pop | |
24 | 8c9ee749 | Michael Hanselmann | import re |
25 | 8c9ee749 | Michael Hanselmann | |
26 | 62e0e880 | Iustin Pop | from ganeti import compat |
27 | 8c9ee749 | Michael Hanselmann | from ganeti import utils |
28 | 8620f50e | Michael Hanselmann | from ganeti import constants |
29 | 8c9ee749 | Michael Hanselmann | |
30 | 8c9ee749 | Michael Hanselmann | |
31 | 8c9ee749 | Michael Hanselmann | _PAREN_RE = re.compile("^[a-zA-Z0-9_-]+$")
|
32 | 8c9ee749 | Michael Hanselmann | |
33 | 8c9ee749 | Michael Hanselmann | |
34 | 8c9ee749 | Michael Hanselmann | def Parens(text): |
35 | 8c9ee749 | Michael Hanselmann | """Enclose text in parens if necessary.
|
36 | 8c9ee749 | Michael Hanselmann |
|
37 | 8c9ee749 | Michael Hanselmann | @param text: Text
|
38 | 8c9ee749 | Michael Hanselmann |
|
39 | 8c9ee749 | Michael Hanselmann | """
|
40 | 8c9ee749 | Michael Hanselmann | text = str(text)
|
41 | 8c9ee749 | Michael Hanselmann | |
42 | 8c9ee749 | Michael Hanselmann | if _PAREN_RE.match(text):
|
43 | 8c9ee749 | Michael Hanselmann | return text
|
44 | 8c9ee749 | Michael Hanselmann | else:
|
45 | 8c9ee749 | Michael Hanselmann | return "(%s)" % text |
46 | 8c9ee749 | Michael Hanselmann | |
47 | 8c9ee749 | Michael Hanselmann | |
48 | 8c9ee749 | Michael Hanselmann | def WithDesc(text): |
49 | 8c9ee749 | Michael Hanselmann | """Builds wrapper class with description text.
|
50 | 8c9ee749 | Michael Hanselmann |
|
51 | 8c9ee749 | Michael Hanselmann | @type text: string
|
52 | 8c9ee749 | Michael Hanselmann | @param text: Description text
|
53 | 8c9ee749 | Michael Hanselmann | @return: Callable class
|
54 | 8c9ee749 | Michael Hanselmann |
|
55 | 8c9ee749 | Michael Hanselmann | """
|
56 | 8c9ee749 | Michael Hanselmann | assert text[0] == text[0].upper() |
57 | 8c9ee749 | Michael Hanselmann | |
58 | 8c9ee749 | Michael Hanselmann | class wrapper(object): # pylint: disable-msg=C0103 |
59 | 8c9ee749 | Michael Hanselmann | __slots__ = ["__call__"]
|
60 | 8c9ee749 | Michael Hanselmann | |
61 | 8c9ee749 | Michael Hanselmann | def __init__(self, fn): |
62 | 8c9ee749 | Michael Hanselmann | """Initializes this class.
|
63 | 8c9ee749 | Michael Hanselmann |
|
64 | 8c9ee749 | Michael Hanselmann | @param fn: Wrapped function
|
65 | 8c9ee749 | Michael Hanselmann |
|
66 | 8c9ee749 | Michael Hanselmann | """
|
67 | 8c9ee749 | Michael Hanselmann | self.__call__ = fn
|
68 | 8c9ee749 | Michael Hanselmann | |
69 | 8c9ee749 | Michael Hanselmann | def __str__(self): |
70 | 8c9ee749 | Michael Hanselmann | return text
|
71 | 8c9ee749 | Michael Hanselmann | |
72 | 8c9ee749 | Michael Hanselmann | return wrapper
|
73 | 8c9ee749 | Michael Hanselmann | |
74 | 8c9ee749 | Michael Hanselmann | |
75 | 8c9ee749 | Michael Hanselmann | def CombinationDesc(op, args, fn): |
76 | 8c9ee749 | Michael Hanselmann | """Build description for combinating operator.
|
77 | 8c9ee749 | Michael Hanselmann |
|
78 | 8c9ee749 | Michael Hanselmann | @type op: string
|
79 | 8c9ee749 | Michael Hanselmann | @param op: Operator as text (e.g. "and")
|
80 | 8c9ee749 | Michael Hanselmann | @type args: list
|
81 | 8c9ee749 | Michael Hanselmann | @param args: Operator arguments
|
82 | 8c9ee749 | Michael Hanselmann | @type fn: callable
|
83 | 8c9ee749 | Michael Hanselmann | @param fn: Wrapped function
|
84 | 8c9ee749 | Michael Hanselmann |
|
85 | 8c9ee749 | Michael Hanselmann | """
|
86 | 8c9ee749 | Michael Hanselmann | if len(args) == 1: |
87 | 8c9ee749 | Michael Hanselmann | descr = str(args[0]) |
88 | 8c9ee749 | Michael Hanselmann | else:
|
89 | 8c9ee749 | Michael Hanselmann | descr = (" %s " % op).join(Parens(i) for i in args) |
90 | 8c9ee749 | Michael Hanselmann | |
91 | 8c9ee749 | Michael Hanselmann | return WithDesc(descr)(fn)
|
92 | 8c9ee749 | Michael Hanselmann | |
93 | 62e0e880 | Iustin Pop | |
94 | 62e0e880 | Iustin Pop | # Modifiable default values; need to define these here before the
|
95 | 62e0e880 | Iustin Pop | # actual LUs
|
96 | 62e0e880 | Iustin Pop | |
97 | 8c9ee749 | Michael Hanselmann | @WithDesc(str([])) |
98 | 62e0e880 | Iustin Pop | def EmptyList(): |
99 | 62e0e880 | Iustin Pop | """Returns an empty list.
|
100 | 62e0e880 | Iustin Pop |
|
101 | 62e0e880 | Iustin Pop | """
|
102 | 62e0e880 | Iustin Pop | return []
|
103 | 62e0e880 | Iustin Pop | |
104 | 62e0e880 | Iustin Pop | |
105 | 8c9ee749 | Michael Hanselmann | @WithDesc(str({})) |
106 | 62e0e880 | Iustin Pop | def EmptyDict(): |
107 | 62e0e880 | Iustin Pop | """Returns an empty dict.
|
108 | 62e0e880 | Iustin Pop |
|
109 | 62e0e880 | Iustin Pop | """
|
110 | 62e0e880 | Iustin Pop | return {}
|
111 | 62e0e880 | Iustin Pop | |
112 | 62e0e880 | Iustin Pop | |
113 | 62e0e880 | Iustin Pop | #: The without-default default value
|
114 | 62e0e880 | Iustin Pop | NoDefault = object()
|
115 | 62e0e880 | Iustin Pop | |
116 | 62e0e880 | Iustin Pop | |
117 | 8c9ee749 | Michael Hanselmann | #: The no-type (value too complex to check it in the type system)
|
118 | 62e0e880 | Iustin Pop | NoType = object()
|
119 | 62e0e880 | Iustin Pop | |
120 | 62e0e880 | Iustin Pop | |
121 | 62e0e880 | Iustin Pop | # Some basic types
|
122 | 8620f50e | Michael Hanselmann | @WithDesc("Anything") |
123 | 8620f50e | Michael Hanselmann | def TAny(_): |
124 | 8620f50e | Michael Hanselmann | """Accepts any value.
|
125 | 8620f50e | Michael Hanselmann |
|
126 | 8620f50e | Michael Hanselmann | """
|
127 | 8620f50e | Michael Hanselmann | return True |
128 | 8620f50e | Michael Hanselmann | |
129 | 8620f50e | Michael Hanselmann | |
130 | 8c9ee749 | Michael Hanselmann | @WithDesc("NotNone") |
131 | 62e0e880 | Iustin Pop | def TNotNone(val): |
132 | 62e0e880 | Iustin Pop | """Checks if the given value is not None.
|
133 | 62e0e880 | Iustin Pop |
|
134 | 62e0e880 | Iustin Pop | """
|
135 | 62e0e880 | Iustin Pop | return val is not None |
136 | 62e0e880 | Iustin Pop | |
137 | 62e0e880 | Iustin Pop | |
138 | 8c9ee749 | Michael Hanselmann | @WithDesc("None") |
139 | 62e0e880 | Iustin Pop | def TNone(val): |
140 | 62e0e880 | Iustin Pop | """Checks if the given value is None.
|
141 | 62e0e880 | Iustin Pop |
|
142 | 62e0e880 | Iustin Pop | """
|
143 | 62e0e880 | Iustin Pop | return val is None |
144 | 62e0e880 | Iustin Pop | |
145 | 62e0e880 | Iustin Pop | |
146 | 8c9ee749 | Michael Hanselmann | @WithDesc("Boolean") |
147 | 62e0e880 | Iustin Pop | def TBool(val): |
148 | 62e0e880 | Iustin Pop | """Checks if the given value is a boolean.
|
149 | 62e0e880 | Iustin Pop |
|
150 | 62e0e880 | Iustin Pop | """
|
151 | 62e0e880 | Iustin Pop | return isinstance(val, bool) |
152 | 62e0e880 | Iustin Pop | |
153 | 62e0e880 | Iustin Pop | |
154 | 8c9ee749 | Michael Hanselmann | @WithDesc("Integer") |
155 | 62e0e880 | Iustin Pop | def TInt(val): |
156 | 62e0e880 | Iustin Pop | """Checks if the given value is an integer.
|
157 | 62e0e880 | Iustin Pop |
|
158 | 62e0e880 | Iustin Pop | """
|
159 | 8568de9e | Michael Hanselmann | # For backwards compatibility with older Python versions, boolean values are
|
160 | 8568de9e | Michael Hanselmann | # also integers and should be excluded in this test.
|
161 | 8568de9e | Michael Hanselmann | #
|
162 | 8568de9e | Michael Hanselmann | # >>> (isinstance(False, int), isinstance(True, int))
|
163 | 8568de9e | Michael Hanselmann | # (True, True)
|
164 | b99b607f | Michael Hanselmann | return isinstance(val, (int, long)) and not isinstance(val, bool) |
165 | 62e0e880 | Iustin Pop | |
166 | 62e0e880 | Iustin Pop | |
167 | 8c9ee749 | Michael Hanselmann | @WithDesc("Float") |
168 | 62e0e880 | Iustin Pop | def TFloat(val): |
169 | 62e0e880 | Iustin Pop | """Checks if the given value is a float.
|
170 | 62e0e880 | Iustin Pop |
|
171 | 62e0e880 | Iustin Pop | """
|
172 | 62e0e880 | Iustin Pop | return isinstance(val, float) |
173 | 62e0e880 | Iustin Pop | |
174 | 62e0e880 | Iustin Pop | |
175 | 8c9ee749 | Michael Hanselmann | @WithDesc("String") |
176 | 62e0e880 | Iustin Pop | def TString(val): |
177 | 62e0e880 | Iustin Pop | """Checks if the given value is a string.
|
178 | 62e0e880 | Iustin Pop |
|
179 | 62e0e880 | Iustin Pop | """
|
180 | 62e0e880 | Iustin Pop | return isinstance(val, basestring) |
181 | 62e0e880 | Iustin Pop | |
182 | 62e0e880 | Iustin Pop | |
183 | 8c9ee749 | Michael Hanselmann | @WithDesc("EvalToTrue") |
184 | 62e0e880 | Iustin Pop | def TTrue(val): |
185 | 62e0e880 | Iustin Pop | """Checks if a given value evaluates to a boolean True value.
|
186 | 62e0e880 | Iustin Pop |
|
187 | 62e0e880 | Iustin Pop | """
|
188 | 62e0e880 | Iustin Pop | return bool(val) |
189 | 62e0e880 | Iustin Pop | |
190 | 62e0e880 | Iustin Pop | |
191 | 62e0e880 | Iustin Pop | def TElemOf(target_list): |
192 | 62e0e880 | Iustin Pop | """Builds a function that checks if a given value is a member of a list.
|
193 | 62e0e880 | Iustin Pop |
|
194 | 62e0e880 | Iustin Pop | """
|
195 | 8c9ee749 | Michael Hanselmann | def fn(val): |
196 | 8c9ee749 | Michael Hanselmann | return val in target_list |
197 | 8c9ee749 | Michael Hanselmann | |
198 | 8c9ee749 | Michael Hanselmann | return WithDesc("OneOf %s" % (utils.CommaJoin(target_list), ))(fn) |
199 | 62e0e880 | Iustin Pop | |
200 | 62e0e880 | Iustin Pop | |
201 | 62e0e880 | Iustin Pop | # Container types
|
202 | 8c9ee749 | Michael Hanselmann | @WithDesc("List") |
203 | 62e0e880 | Iustin Pop | def TList(val): |
204 | 62e0e880 | Iustin Pop | """Checks if the given value is a list.
|
205 | 62e0e880 | Iustin Pop |
|
206 | 62e0e880 | Iustin Pop | """
|
207 | 62e0e880 | Iustin Pop | return isinstance(val, list) |
208 | 62e0e880 | Iustin Pop | |
209 | 62e0e880 | Iustin Pop | |
210 | 8c9ee749 | Michael Hanselmann | @WithDesc("Dictionary") |
211 | 62e0e880 | Iustin Pop | def TDict(val): |
212 | 62e0e880 | Iustin Pop | """Checks if the given value is a dictionary.
|
213 | 62e0e880 | Iustin Pop |
|
214 | 62e0e880 | Iustin Pop | """
|
215 | 62e0e880 | Iustin Pop | return isinstance(val, dict) |
216 | 62e0e880 | Iustin Pop | |
217 | 62e0e880 | Iustin Pop | |
218 | 62e0e880 | Iustin Pop | def TIsLength(size): |
219 | 62e0e880 | Iustin Pop | """Check is the given container is of the given size.
|
220 | 62e0e880 | Iustin Pop |
|
221 | 62e0e880 | Iustin Pop | """
|
222 | 8c9ee749 | Michael Hanselmann | def fn(container): |
223 | 8c9ee749 | Michael Hanselmann | return len(container) == size |
224 | 8c9ee749 | Michael Hanselmann | |
225 | 8c9ee749 | Michael Hanselmann | return WithDesc("Length %s" % (size, ))(fn) |
226 | 62e0e880 | Iustin Pop | |
227 | 62e0e880 | Iustin Pop | |
228 | 62e0e880 | Iustin Pop | # Combinator types
|
229 | 62e0e880 | Iustin Pop | def TAnd(*args): |
230 | 62e0e880 | Iustin Pop | """Combine multiple functions using an AND operation.
|
231 | 62e0e880 | Iustin Pop |
|
232 | 62e0e880 | Iustin Pop | """
|
233 | 62e0e880 | Iustin Pop | def fn(val): |
234 | 62e0e880 | Iustin Pop | return compat.all(t(val) for t in args) |
235 | 8c9ee749 | Michael Hanselmann | |
236 | 8c9ee749 | Michael Hanselmann | return CombinationDesc("and", args, fn) |
237 | 62e0e880 | Iustin Pop | |
238 | 62e0e880 | Iustin Pop | |
239 | 62e0e880 | Iustin Pop | def TOr(*args): |
240 | 62e0e880 | Iustin Pop | """Combine multiple functions using an AND operation.
|
241 | 62e0e880 | Iustin Pop |
|
242 | 62e0e880 | Iustin Pop | """
|
243 | 62e0e880 | Iustin Pop | def fn(val): |
244 | 62e0e880 | Iustin Pop | return compat.any(t(val) for t in args) |
245 | 8c9ee749 | Michael Hanselmann | |
246 | 8c9ee749 | Michael Hanselmann | return CombinationDesc("or", args, fn) |
247 | 62e0e880 | Iustin Pop | |
248 | 62e0e880 | Iustin Pop | |
249 | 62e0e880 | Iustin Pop | def TMap(fn, test): |
250 | 62e0e880 | Iustin Pop | """Checks that a modified version of the argument passes the given test.
|
251 | 62e0e880 | Iustin Pop |
|
252 | 62e0e880 | Iustin Pop | """
|
253 | 8c9ee749 | Michael Hanselmann | return WithDesc("Result of %s must be %s" % |
254 | 8c9ee749 | Michael Hanselmann | (Parens(fn), Parens(test)))(lambda val: test(fn(val)))
|
255 | 62e0e880 | Iustin Pop | |
256 | 62e0e880 | Iustin Pop | |
257 | 8620f50e | Michael Hanselmann | def TRegex(pobj): |
258 | 8620f50e | Michael Hanselmann | """Checks whether a string matches a specific regular expression.
|
259 | 8620f50e | Michael Hanselmann |
|
260 | 8620f50e | Michael Hanselmann | @param pobj: Compiled regular expression as returned by C{re.compile}
|
261 | 8620f50e | Michael Hanselmann |
|
262 | 8620f50e | Michael Hanselmann | """
|
263 | 8620f50e | Michael Hanselmann | desc = WithDesc("String matching regex \"%s\"" %
|
264 | 8620f50e | Michael Hanselmann | pobj.pattern.encode("string_escape"))
|
265 | 8620f50e | Michael Hanselmann | |
266 | 8620f50e | Michael Hanselmann | return desc(TAnd(TString, pobj.match))
|
267 | 8620f50e | Michael Hanselmann | |
268 | 8620f50e | Michael Hanselmann | |
269 | 62e0e880 | Iustin Pop | # Type aliases
|
270 | 62e0e880 | Iustin Pop | |
271 | 62e0e880 | Iustin Pop | #: a non-empty string
|
272 | 8c9ee749 | Michael Hanselmann | TNonEmptyString = WithDesc("NonEmptyString")(TAnd(TString, TTrue))
|
273 | 62e0e880 | Iustin Pop | |
274 | 62e0e880 | Iustin Pop | #: a maybe non-empty string
|
275 | 62e0e880 | Iustin Pop | TMaybeString = TOr(TNonEmptyString, TNone) |
276 | 62e0e880 | Iustin Pop | |
277 | 62e0e880 | Iustin Pop | #: a maybe boolean (bool or none)
|
278 | 62e0e880 | Iustin Pop | TMaybeBool = TOr(TBool, TNone) |
279 | 62e0e880 | Iustin Pop | |
280 | 5f074973 | Michael Hanselmann | #: Maybe a dictionary (dict or None)
|
281 | 5f074973 | Michael Hanselmann | TMaybeDict = TOr(TDict, TNone) |
282 | 62e0e880 | Iustin Pop | |
283 | 62e0e880 | Iustin Pop | #: a positive integer
|
284 | 8c9ee749 | Michael Hanselmann | TPositiveInt = \ |
285 | 8c9ee749 | Michael Hanselmann | TAnd(TInt, WithDesc("EqualGreaterZero")(lambda v: v >= 0)) |
286 | 62e0e880 | Iustin Pop | |
287 | 62e0e880 | Iustin Pop | #: a strictly positive integer
|
288 | 8c9ee749 | Michael Hanselmann | TStrictPositiveInt = \ |
289 | 8c9ee749 | Michael Hanselmann | TAnd(TInt, WithDesc("GreaterThanZero")(lambda v: v > 0)) |
290 | 62e0e880 | Iustin Pop | |
291 | beff3779 | René Nussbaumer | #: a positive float
|
292 | beff3779 | René Nussbaumer | TPositiveFloat = \ |
293 | beff3779 | René Nussbaumer | TAnd(TFloat, WithDesc("EqualGreaterZero")(lambda v: v >= 0.0)) |
294 | beff3779 | René Nussbaumer | |
295 | 8620f50e | Michael Hanselmann | #: Job ID
|
296 | 8620f50e | Michael Hanselmann | TJobId = TOr(TPositiveInt, |
297 | 8620f50e | Michael Hanselmann | TRegex(re.compile("^%s$" % constants.JOB_ID_TEMPLATE)))
|
298 | 8620f50e | Michael Hanselmann | |
299 | 62e0e880 | Iustin Pop | |
300 | 62e0e880 | Iustin Pop | def TListOf(my_type): |
301 | 62e0e880 | Iustin Pop | """Checks if a given value is a list with all elements of the same type.
|
302 | 62e0e880 | Iustin Pop |
|
303 | 62e0e880 | Iustin Pop | """
|
304 | 8c9ee749 | Michael Hanselmann | desc = WithDesc("List of %s" % (Parens(my_type), ))
|
305 | 8c9ee749 | Michael Hanselmann | return desc(TAnd(TList, lambda lst: compat.all(my_type(v) for v in lst))) |
306 | 62e0e880 | Iustin Pop | |
307 | 62e0e880 | Iustin Pop | |
308 | 62e0e880 | Iustin Pop | def TDictOf(key_type, val_type): |
309 | 62e0e880 | Iustin Pop | """Checks a dict type for the type of its key/values.
|
310 | 62e0e880 | Iustin Pop |
|
311 | 62e0e880 | Iustin Pop | """
|
312 | 8c9ee749 | Michael Hanselmann | desc = WithDesc("Dictionary with keys of %s and values of %s" %
|
313 | 8c9ee749 | Michael Hanselmann | (Parens(key_type), Parens(val_type))) |
314 | 8c9ee749 | Michael Hanselmann | |
315 | 8c9ee749 | Michael Hanselmann | def fn(container): |
316 | 8c9ee749 | Michael Hanselmann | return (compat.all(key_type(v) for v in container.keys()) and |
317 | 8c9ee749 | Michael Hanselmann | compat.all(val_type(v) for v in container.values())) |
318 | 8c9ee749 | Michael Hanselmann | |
319 | 8c9ee749 | Michael Hanselmann | return desc(TAnd(TDict, fn))
|
320 | a464ce71 | Michael Hanselmann | |
321 | a464ce71 | Michael Hanselmann | |
322 | a464ce71 | Michael Hanselmann | def _TStrictDictCheck(require_all, exclusive, items, val): |
323 | a464ce71 | Michael Hanselmann | """Helper function for L{TStrictDict}.
|
324 | a464ce71 | Michael Hanselmann |
|
325 | a464ce71 | Michael Hanselmann | """
|
326 | a464ce71 | Michael Hanselmann | notfound_fn = lambda _: not exclusive |
327 | a464ce71 | Michael Hanselmann | |
328 | a464ce71 | Michael Hanselmann | if require_all and not frozenset(val.keys()).issuperset(items.keys()): |
329 | a464ce71 | Michael Hanselmann | # Requires items not found in value
|
330 | a464ce71 | Michael Hanselmann | return False |
331 | a464ce71 | Michael Hanselmann | |
332 | a464ce71 | Michael Hanselmann | return compat.all(items.get(key, notfound_fn)(value)
|
333 | a464ce71 | Michael Hanselmann | for (key, value) in val.items()) |
334 | a464ce71 | Michael Hanselmann | |
335 | a464ce71 | Michael Hanselmann | |
336 | a464ce71 | Michael Hanselmann | def TStrictDict(require_all, exclusive, items): |
337 | a464ce71 | Michael Hanselmann | """Strict dictionary check with specific keys.
|
338 | a464ce71 | Michael Hanselmann |
|
339 | a464ce71 | Michael Hanselmann | @type require_all: boolean
|
340 | a464ce71 | Michael Hanselmann | @param require_all: Whether all keys in L{items} are required
|
341 | a464ce71 | Michael Hanselmann | @type exclusive: boolean
|
342 | a464ce71 | Michael Hanselmann | @param exclusive: Whether only keys listed in L{items} should be accepted
|
343 | a464ce71 | Michael Hanselmann | @type items: dictionary
|
344 | a464ce71 | Michael Hanselmann | @param items: Mapping from key (string) to verification function
|
345 | a464ce71 | Michael Hanselmann |
|
346 | a464ce71 | Michael Hanselmann | """
|
347 | a464ce71 | Michael Hanselmann | descparts = ["Dictionary containing"]
|
348 | a464ce71 | Michael Hanselmann | |
349 | a464ce71 | Michael Hanselmann | if exclusive:
|
350 | a464ce71 | Michael Hanselmann | descparts.append(" none but the")
|
351 | a464ce71 | Michael Hanselmann | |
352 | a464ce71 | Michael Hanselmann | if require_all:
|
353 | a464ce71 | Michael Hanselmann | descparts.append(" required")
|
354 | a464ce71 | Michael Hanselmann | |
355 | a464ce71 | Michael Hanselmann | if len(items) == 1: |
356 | a464ce71 | Michael Hanselmann | descparts.append(" key ")
|
357 | a464ce71 | Michael Hanselmann | else:
|
358 | a464ce71 | Michael Hanselmann | descparts.append(" keys ")
|
359 | a464ce71 | Michael Hanselmann | |
360 | a464ce71 | Michael Hanselmann | descparts.append(utils.CommaJoin("\"%s\" (value %s)" % (key, value)
|
361 | a464ce71 | Michael Hanselmann | for (key, value) in items.items())) |
362 | a464ce71 | Michael Hanselmann | |
363 | a464ce71 | Michael Hanselmann | desc = WithDesc("".join(descparts))
|
364 | a464ce71 | Michael Hanselmann | |
365 | a464ce71 | Michael Hanselmann | return desc(TAnd(TDict,
|
366 | a464ce71 | Michael Hanselmann | compat.partial(_TStrictDictCheck, require_all, exclusive, |
367 | a464ce71 | Michael Hanselmann | items))) |
368 | 8620f50e | Michael Hanselmann | |
369 | 8620f50e | Michael Hanselmann | |
370 | 8620f50e | Michael Hanselmann | def TItems(items): |
371 | 8620f50e | Michael Hanselmann | """Checks individual items of a container.
|
372 | 8620f50e | Michael Hanselmann |
|
373 | 8620f50e | Michael Hanselmann | If the verified value and the list of expected items differ in length, this
|
374 | 8620f50e | Michael Hanselmann | check considers only as many items as are contained in the shorter list. Use
|
375 | 8620f50e | Michael Hanselmann | L{TIsLength} to enforce a certain length.
|
376 | 8620f50e | Michael Hanselmann |
|
377 | 8620f50e | Michael Hanselmann | @type items: list
|
378 | 8620f50e | Michael Hanselmann | @param items: List of checks
|
379 | 8620f50e | Michael Hanselmann |
|
380 | 8620f50e | Michael Hanselmann | """
|
381 | 8620f50e | Michael Hanselmann | assert items, "Need items" |
382 | 8620f50e | Michael Hanselmann | |
383 | 8620f50e | Michael Hanselmann | text = ["Item", "item"] |
384 | 8620f50e | Michael Hanselmann | desc = WithDesc(utils.CommaJoin("%s %s is %s" %
|
385 | 8620f50e | Michael Hanselmann | (text[int(idx > 0)], idx, Parens(check)) |
386 | 8620f50e | Michael Hanselmann | for (idx, check) in enumerate(items))) |
387 | 8620f50e | Michael Hanselmann | |
388 | 8620f50e | Michael Hanselmann | return desc(lambda value: compat.all(check(i) |
389 | 8620f50e | Michael Hanselmann | for (check, i) in zip(items, value))) |