Statistics
| Branch: | Tag: | Revision:

root / lib / ht.py @ 63b4bb1e

History | View | Annotate | Download (9.7 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 b247c6fc Michael Hanselmann
import operator
26 8c9ee749 Michael Hanselmann
27 62e0e880 Iustin Pop
from ganeti import compat
28 8c9ee749 Michael Hanselmann
from ganeti import utils
29 8620f50e Michael Hanselmann
from ganeti import constants
30 8c9ee749 Michael Hanselmann
31 8c9ee749 Michael Hanselmann
32 8c9ee749 Michael Hanselmann
_PAREN_RE = re.compile("^[a-zA-Z0-9_-]+$")
33 8c9ee749 Michael Hanselmann
34 8c9ee749 Michael Hanselmann
35 8c9ee749 Michael Hanselmann
def Parens(text):
36 8c9ee749 Michael Hanselmann
  """Enclose text in parens if necessary.
37 8c9ee749 Michael Hanselmann

38 8c9ee749 Michael Hanselmann
  @param text: Text
39 8c9ee749 Michael Hanselmann

40 8c9ee749 Michael Hanselmann
  """
41 8c9ee749 Michael Hanselmann
  text = str(text)
42 8c9ee749 Michael Hanselmann
43 8c9ee749 Michael Hanselmann
  if _PAREN_RE.match(text):
44 8c9ee749 Michael Hanselmann
    return text
45 8c9ee749 Michael Hanselmann
  else:
46 8c9ee749 Michael Hanselmann
    return "(%s)" % text
47 8c9ee749 Michael Hanselmann
48 8c9ee749 Michael Hanselmann
49 9eec6d67 Michael Hanselmann
class _WrapperBase(object):
50 7fc548e9 Michael Hanselmann
  __slots__ = [
51 7fc548e9 Michael Hanselmann
    "_fn",
52 7fc548e9 Michael Hanselmann
    "_text",
53 7fc548e9 Michael Hanselmann
    ]
54 7fc548e9 Michael Hanselmann
55 7fc548e9 Michael Hanselmann
  def __init__(self, text, fn):
56 7fc548e9 Michael Hanselmann
    """Initializes this class.
57 7fc548e9 Michael Hanselmann

58 7fc548e9 Michael Hanselmann
    @param text: Description
59 7fc548e9 Michael Hanselmann
    @param fn: Wrapped function
60 7fc548e9 Michael Hanselmann

61 7fc548e9 Michael Hanselmann
    """
62 9eec6d67 Michael Hanselmann
    assert text.strip()
63 9eec6d67 Michael Hanselmann
64 7fc548e9 Michael Hanselmann
    self._text = text
65 7fc548e9 Michael Hanselmann
    self._fn = fn
66 7fc548e9 Michael Hanselmann
67 7fc548e9 Michael Hanselmann
  def __call__(self, *args):
68 7fc548e9 Michael Hanselmann
    return self._fn(*args)
69 7fc548e9 Michael Hanselmann
70 9eec6d67 Michael Hanselmann
71 9eec6d67 Michael Hanselmann
class _DescWrapper(_WrapperBase):
72 9eec6d67 Michael Hanselmann
  """Wrapper class for description text.
73 9eec6d67 Michael Hanselmann

74 9eec6d67 Michael Hanselmann
  """
75 7fc548e9 Michael Hanselmann
  def __str__(self):
76 7fc548e9 Michael Hanselmann
    return self._text
77 7fc548e9 Michael Hanselmann
78 7fc548e9 Michael Hanselmann
79 9eec6d67 Michael Hanselmann
class _CommentWrapper(_WrapperBase):
80 9eec6d67 Michael Hanselmann
  """Wrapper class for comment.
81 9eec6d67 Michael Hanselmann

82 9eec6d67 Michael Hanselmann
  """
83 9eec6d67 Michael Hanselmann
  def __str__(self):
84 9eec6d67 Michael Hanselmann
    return "%s [%s]" % (self._fn, self._text)
85 9eec6d67 Michael Hanselmann
86 9eec6d67 Michael Hanselmann
87 8c9ee749 Michael Hanselmann
def WithDesc(text):
88 8c9ee749 Michael Hanselmann
  """Builds wrapper class with description text.
89 8c9ee749 Michael Hanselmann

90 8c9ee749 Michael Hanselmann
  @type text: string
91 8c9ee749 Michael Hanselmann
  @param text: Description text
92 8c9ee749 Michael Hanselmann
  @return: Callable class
93 8c9ee749 Michael Hanselmann

94 8c9ee749 Michael Hanselmann
  """
95 8c9ee749 Michael Hanselmann
  assert text[0] == text[0].upper()
96 8c9ee749 Michael Hanselmann
97 7fc548e9 Michael Hanselmann
  return compat.partial(_DescWrapper, text)
98 8c9ee749 Michael Hanselmann
99 8c9ee749 Michael Hanselmann
100 9eec6d67 Michael Hanselmann
def Comment(text):
101 9eec6d67 Michael Hanselmann
  """Builds wrapper for adding comment to description text.
102 9eec6d67 Michael Hanselmann

103 9eec6d67 Michael Hanselmann
  @type text: string
104 9eec6d67 Michael Hanselmann
  @param text: Comment text
105 9eec6d67 Michael Hanselmann
  @return: Callable class
106 9eec6d67 Michael Hanselmann

107 9eec6d67 Michael Hanselmann
  """
108 9eec6d67 Michael Hanselmann
  assert not frozenset(text).intersection("[]")
109 9eec6d67 Michael Hanselmann
110 9eec6d67 Michael Hanselmann
  return compat.partial(_CommentWrapper, text)
111 9eec6d67 Michael Hanselmann
112 9eec6d67 Michael Hanselmann
113 8c9ee749 Michael Hanselmann
def CombinationDesc(op, args, fn):
114 8c9ee749 Michael Hanselmann
  """Build description for combinating operator.
115 8c9ee749 Michael Hanselmann

116 8c9ee749 Michael Hanselmann
  @type op: string
117 8c9ee749 Michael Hanselmann
  @param op: Operator as text (e.g. "and")
118 8c9ee749 Michael Hanselmann
  @type args: list
119 8c9ee749 Michael Hanselmann
  @param args: Operator arguments
120 8c9ee749 Michael Hanselmann
  @type fn: callable
121 8c9ee749 Michael Hanselmann
  @param fn: Wrapped function
122 8c9ee749 Michael Hanselmann

123 8c9ee749 Michael Hanselmann
  """
124 8c9ee749 Michael Hanselmann
  if len(args) == 1:
125 8c9ee749 Michael Hanselmann
    descr = str(args[0])
126 8c9ee749 Michael Hanselmann
  else:
127 8c9ee749 Michael Hanselmann
    descr = (" %s " % op).join(Parens(i) for i in args)
128 8c9ee749 Michael Hanselmann
129 8c9ee749 Michael Hanselmann
  return WithDesc(descr)(fn)
130 8c9ee749 Michael Hanselmann
131 62e0e880 Iustin Pop
132 62e0e880 Iustin Pop
# Modifiable default values; need to define these here before the
133 62e0e880 Iustin Pop
# actual LUs
134 62e0e880 Iustin Pop
135 8c9ee749 Michael Hanselmann
@WithDesc(str([]))
136 62e0e880 Iustin Pop
def EmptyList():
137 62e0e880 Iustin Pop
  """Returns an empty list.
138 62e0e880 Iustin Pop

139 62e0e880 Iustin Pop
  """
140 62e0e880 Iustin Pop
  return []
141 62e0e880 Iustin Pop
142 62e0e880 Iustin Pop
143 8c9ee749 Michael Hanselmann
@WithDesc(str({}))
144 62e0e880 Iustin Pop
def EmptyDict():
145 62e0e880 Iustin Pop
  """Returns an empty dict.
146 62e0e880 Iustin Pop

147 62e0e880 Iustin Pop
  """
148 62e0e880 Iustin Pop
  return {}
149 62e0e880 Iustin Pop
150 62e0e880 Iustin Pop
151 62e0e880 Iustin Pop
#: The without-default default value
152 62e0e880 Iustin Pop
NoDefault = object()
153 62e0e880 Iustin Pop
154 62e0e880 Iustin Pop
155 8c9ee749 Michael Hanselmann
#: The no-type (value too complex to check it in the type system)
156 62e0e880 Iustin Pop
NoType = object()
157 62e0e880 Iustin Pop
158 62e0e880 Iustin Pop
159 62e0e880 Iustin Pop
# Some basic types
160 8620f50e Michael Hanselmann
@WithDesc("Anything")
161 8620f50e Michael Hanselmann
def TAny(_):
162 8620f50e Michael Hanselmann
  """Accepts any value.
163 8620f50e Michael Hanselmann

164 8620f50e Michael Hanselmann
  """
165 8620f50e Michael Hanselmann
  return True
166 8620f50e Michael Hanselmann
167 8620f50e Michael Hanselmann
168 8c9ee749 Michael Hanselmann
@WithDesc("NotNone")
169 62e0e880 Iustin Pop
def TNotNone(val):
170 62e0e880 Iustin Pop
  """Checks if the given value is not None.
171 62e0e880 Iustin Pop

172 62e0e880 Iustin Pop
  """
173 62e0e880 Iustin Pop
  return val is not None
174 62e0e880 Iustin Pop
175 62e0e880 Iustin Pop
176 8c9ee749 Michael Hanselmann
@WithDesc("None")
177 62e0e880 Iustin Pop
def TNone(val):
178 62e0e880 Iustin Pop
  """Checks if the given value is None.
179 62e0e880 Iustin Pop

180 62e0e880 Iustin Pop
  """
181 62e0e880 Iustin Pop
  return val is None
182 62e0e880 Iustin Pop
183 62e0e880 Iustin Pop
184 8c9ee749 Michael Hanselmann
@WithDesc("Boolean")
185 62e0e880 Iustin Pop
def TBool(val):
186 62e0e880 Iustin Pop
  """Checks if the given value is a boolean.
187 62e0e880 Iustin Pop

188 62e0e880 Iustin Pop
  """
189 62e0e880 Iustin Pop
  return isinstance(val, bool)
190 62e0e880 Iustin Pop
191 62e0e880 Iustin Pop
192 8c9ee749 Michael Hanselmann
@WithDesc("Integer")
193 62e0e880 Iustin Pop
def TInt(val):
194 62e0e880 Iustin Pop
  """Checks if the given value is an integer.
195 62e0e880 Iustin Pop

196 62e0e880 Iustin Pop
  """
197 8568de9e Michael Hanselmann
  # For backwards compatibility with older Python versions, boolean values are
198 8568de9e Michael Hanselmann
  # also integers and should be excluded in this test.
199 8568de9e Michael Hanselmann
  #
200 8568de9e Michael Hanselmann
  # >>> (isinstance(False, int), isinstance(True, int))
201 8568de9e Michael Hanselmann
  # (True, True)
202 b99b607f Michael Hanselmann
  return isinstance(val, (int, long)) and not isinstance(val, bool)
203 62e0e880 Iustin Pop
204 62e0e880 Iustin Pop
205 8c9ee749 Michael Hanselmann
@WithDesc("Float")
206 62e0e880 Iustin Pop
def TFloat(val):
207 62e0e880 Iustin Pop
  """Checks if the given value is a float.
208 62e0e880 Iustin Pop

209 62e0e880 Iustin Pop
  """
210 62e0e880 Iustin Pop
  return isinstance(val, float)
211 62e0e880 Iustin Pop
212 62e0e880 Iustin Pop
213 8c9ee749 Michael Hanselmann
@WithDesc("String")
214 62e0e880 Iustin Pop
def TString(val):
215 62e0e880 Iustin Pop
  """Checks if the given value is a string.
216 62e0e880 Iustin Pop

217 62e0e880 Iustin Pop
  """
218 62e0e880 Iustin Pop
  return isinstance(val, basestring)
219 62e0e880 Iustin Pop
220 62e0e880 Iustin Pop
221 8c9ee749 Michael Hanselmann
@WithDesc("EvalToTrue")
222 62e0e880 Iustin Pop
def TTrue(val):
223 62e0e880 Iustin Pop
  """Checks if a given value evaluates to a boolean True value.
224 62e0e880 Iustin Pop

225 62e0e880 Iustin Pop
  """
226 62e0e880 Iustin Pop
  return bool(val)
227 62e0e880 Iustin Pop
228 62e0e880 Iustin Pop
229 62e0e880 Iustin Pop
def TElemOf(target_list):
230 62e0e880 Iustin Pop
  """Builds a function that checks if a given value is a member of a list.
231 62e0e880 Iustin Pop

232 62e0e880 Iustin Pop
  """
233 8c9ee749 Michael Hanselmann
  def fn(val):
234 8c9ee749 Michael Hanselmann
    return val in target_list
235 8c9ee749 Michael Hanselmann
236 8c9ee749 Michael Hanselmann
  return WithDesc("OneOf %s" % (utils.CommaJoin(target_list), ))(fn)
237 62e0e880 Iustin Pop
238 62e0e880 Iustin Pop
239 62e0e880 Iustin Pop
# Container types
240 8c9ee749 Michael Hanselmann
@WithDesc("List")
241 62e0e880 Iustin Pop
def TList(val):
242 62e0e880 Iustin Pop
  """Checks if the given value is a list.
243 62e0e880 Iustin Pop

244 62e0e880 Iustin Pop
  """
245 62e0e880 Iustin Pop
  return isinstance(val, list)
246 62e0e880 Iustin Pop
247 62e0e880 Iustin Pop
248 8c9ee749 Michael Hanselmann
@WithDesc("Dictionary")
249 62e0e880 Iustin Pop
def TDict(val):
250 62e0e880 Iustin Pop
  """Checks if the given value is a dictionary.
251 62e0e880 Iustin Pop

252 62e0e880 Iustin Pop
  """
253 62e0e880 Iustin Pop
  return isinstance(val, dict)
254 62e0e880 Iustin Pop
255 62e0e880 Iustin Pop
256 62e0e880 Iustin Pop
def TIsLength(size):
257 62e0e880 Iustin Pop
  """Check is the given container is of the given size.
258 62e0e880 Iustin Pop

259 62e0e880 Iustin Pop
  """
260 8c9ee749 Michael Hanselmann
  def fn(container):
261 8c9ee749 Michael Hanselmann
    return len(container) == size
262 8c9ee749 Michael Hanselmann
263 8c9ee749 Michael Hanselmann
  return WithDesc("Length %s" % (size, ))(fn)
264 62e0e880 Iustin Pop
265 62e0e880 Iustin Pop
266 62e0e880 Iustin Pop
# Combinator types
267 62e0e880 Iustin Pop
def TAnd(*args):
268 62e0e880 Iustin Pop
  """Combine multiple functions using an AND operation.
269 62e0e880 Iustin Pop

270 62e0e880 Iustin Pop
  """
271 62e0e880 Iustin Pop
  def fn(val):
272 62e0e880 Iustin Pop
    return compat.all(t(val) for t in args)
273 8c9ee749 Michael Hanselmann
274 8c9ee749 Michael Hanselmann
  return CombinationDesc("and", args, fn)
275 62e0e880 Iustin Pop
276 62e0e880 Iustin Pop
277 62e0e880 Iustin Pop
def TOr(*args):
278 62e0e880 Iustin Pop
  """Combine multiple functions using an AND operation.
279 62e0e880 Iustin Pop

280 62e0e880 Iustin Pop
  """
281 62e0e880 Iustin Pop
  def fn(val):
282 62e0e880 Iustin Pop
    return compat.any(t(val) for t in args)
283 8c9ee749 Michael Hanselmann
284 8c9ee749 Michael Hanselmann
  return CombinationDesc("or", args, fn)
285 62e0e880 Iustin Pop
286 62e0e880 Iustin Pop
287 62e0e880 Iustin Pop
def TMap(fn, test):
288 62e0e880 Iustin Pop
  """Checks that a modified version of the argument passes the given test.
289 62e0e880 Iustin Pop

290 62e0e880 Iustin Pop
  """
291 8c9ee749 Michael Hanselmann
  return WithDesc("Result of %s must be %s" %
292 8c9ee749 Michael Hanselmann
                  (Parens(fn), Parens(test)))(lambda val: test(fn(val)))
293 62e0e880 Iustin Pop
294 62e0e880 Iustin Pop
295 8620f50e Michael Hanselmann
def TRegex(pobj):
296 8620f50e Michael Hanselmann
  """Checks whether a string matches a specific regular expression.
297 8620f50e Michael Hanselmann

298 8620f50e Michael Hanselmann
  @param pobj: Compiled regular expression as returned by C{re.compile}
299 8620f50e Michael Hanselmann

300 8620f50e Michael Hanselmann
  """
301 8620f50e Michael Hanselmann
  desc = WithDesc("String matching regex \"%s\"" %
302 8620f50e Michael Hanselmann
                  pobj.pattern.encode("string_escape"))
303 8620f50e Michael Hanselmann
304 8620f50e Michael Hanselmann
  return desc(TAnd(TString, pobj.match))
305 8620f50e Michael Hanselmann
306 8620f50e Michael Hanselmann
307 62e0e880 Iustin Pop
# Type aliases
308 62e0e880 Iustin Pop
309 62e0e880 Iustin Pop
#: a non-empty string
310 8c9ee749 Michael Hanselmann
TNonEmptyString = WithDesc("NonEmptyString")(TAnd(TString, TTrue))
311 62e0e880 Iustin Pop
312 62e0e880 Iustin Pop
#: a maybe non-empty string
313 62e0e880 Iustin Pop
TMaybeString = TOr(TNonEmptyString, TNone)
314 62e0e880 Iustin Pop
315 62e0e880 Iustin Pop
#: a maybe boolean (bool or none)
316 62e0e880 Iustin Pop
TMaybeBool = TOr(TBool, TNone)
317 62e0e880 Iustin Pop
318 5f074973 Michael Hanselmann
#: Maybe a dictionary (dict or None)
319 5f074973 Michael Hanselmann
TMaybeDict = TOr(TDict, TNone)
320 62e0e880 Iustin Pop
321 62e0e880 Iustin Pop
#: a positive integer
322 8c9ee749 Michael Hanselmann
TPositiveInt = \
323 8c9ee749 Michael Hanselmann
  TAnd(TInt, WithDesc("EqualGreaterZero")(lambda v: v >= 0))
324 62e0e880 Iustin Pop
325 2c0af7da Guido Trotter
#: a maybe positive integer (positive integer or None)
326 2c0af7da Guido Trotter
TMaybePositiveInt = TOr(TPositiveInt, TNone)
327 2c0af7da Guido Trotter
328 62e0e880 Iustin Pop
#: a strictly positive integer
329 8c9ee749 Michael Hanselmann
TStrictPositiveInt = \
330 8c9ee749 Michael Hanselmann
  TAnd(TInt, WithDesc("GreaterThanZero")(lambda v: v > 0))
331 62e0e880 Iustin Pop
332 2c0af7da Guido Trotter
#: a maybe strictly positive integer (strictly positive integer or None)
333 2c0af7da Guido Trotter
TMaybeStrictPositiveInt = TOr(TStrictPositiveInt, TNone)
334 2c0af7da Guido Trotter
335 b247c6fc Michael Hanselmann
#: a strictly negative integer (0 > value)
336 b247c6fc Michael Hanselmann
TStrictNegativeInt = \
337 b247c6fc Michael Hanselmann
  TAnd(TInt, WithDesc("LessThanZero")(compat.partial(operator.gt, 0)))
338 b247c6fc Michael Hanselmann
339 beff3779 René Nussbaumer
#: a positive float
340 beff3779 René Nussbaumer
TPositiveFloat = \
341 beff3779 René Nussbaumer
  TAnd(TFloat, WithDesc("EqualGreaterZero")(lambda v: v >= 0.0))
342 beff3779 René Nussbaumer
343 8620f50e Michael Hanselmann
#: Job ID
344 bdfd7802 Michael Hanselmann
TJobId = WithDesc("JobId")(TOr(TPositiveInt,
345 bdfd7802 Michael Hanselmann
                               TRegex(re.compile("^%s$" %
346 bdfd7802 Michael Hanselmann
                                                 constants.JOB_ID_TEMPLATE))))
347 8620f50e Michael Hanselmann
348 697f49d5 Michael Hanselmann
#: Number
349 697f49d5 Michael Hanselmann
TNumber = TOr(TInt, TFloat)
350 697f49d5 Michael Hanselmann
351 b247c6fc Michael Hanselmann
#: Relative job ID
352 b247c6fc Michael Hanselmann
TRelativeJobId = WithDesc("RelativeJobId")(TStrictNegativeInt)
353 b247c6fc Michael Hanselmann
354 62e0e880 Iustin Pop
355 62e0e880 Iustin Pop
def TListOf(my_type):
356 62e0e880 Iustin Pop
  """Checks if a given value is a list with all elements of the same type.
357 62e0e880 Iustin Pop

358 62e0e880 Iustin Pop
  """
359 8c9ee749 Michael Hanselmann
  desc = WithDesc("List of %s" % (Parens(my_type), ))
360 8c9ee749 Michael Hanselmann
  return desc(TAnd(TList, lambda lst: compat.all(my_type(v) for v in lst)))
361 62e0e880 Iustin Pop
362 62e0e880 Iustin Pop
363 ff8067cf Michael Hanselmann
TMaybeListOf = lambda item_type: TOr(TNone, TListOf(item_type))
364 ff8067cf Michael Hanselmann
365 ff8067cf Michael Hanselmann
366 62e0e880 Iustin Pop
def TDictOf(key_type, val_type):
367 62e0e880 Iustin Pop
  """Checks a dict type for the type of its key/values.
368 62e0e880 Iustin Pop

369 62e0e880 Iustin Pop
  """
370 8c9ee749 Michael Hanselmann
  desc = WithDesc("Dictionary with keys of %s and values of %s" %
371 8c9ee749 Michael Hanselmann
                  (Parens(key_type), Parens(val_type)))
372 8c9ee749 Michael Hanselmann
373 8c9ee749 Michael Hanselmann
  def fn(container):
374 8c9ee749 Michael Hanselmann
    return (compat.all(key_type(v) for v in container.keys()) and
375 8c9ee749 Michael Hanselmann
            compat.all(val_type(v) for v in container.values()))
376 8c9ee749 Michael Hanselmann
377 8c9ee749 Michael Hanselmann
  return desc(TAnd(TDict, fn))
378 a464ce71 Michael Hanselmann
379 a464ce71 Michael Hanselmann
380 a464ce71 Michael Hanselmann
def _TStrictDictCheck(require_all, exclusive, items, val):
381 a464ce71 Michael Hanselmann
  """Helper function for L{TStrictDict}.
382 a464ce71 Michael Hanselmann

383 a464ce71 Michael Hanselmann
  """
384 a464ce71 Michael Hanselmann
  notfound_fn = lambda _: not exclusive
385 a464ce71 Michael Hanselmann
386 a464ce71 Michael Hanselmann
  if require_all and not frozenset(val.keys()).issuperset(items.keys()):
387 a464ce71 Michael Hanselmann
    # Requires items not found in value
388 a464ce71 Michael Hanselmann
    return False
389 a464ce71 Michael Hanselmann
390 a464ce71 Michael Hanselmann
  return compat.all(items.get(key, notfound_fn)(value)
391 a464ce71 Michael Hanselmann
                    for (key, value) in val.items())
392 a464ce71 Michael Hanselmann
393 a464ce71 Michael Hanselmann
394 a464ce71 Michael Hanselmann
def TStrictDict(require_all, exclusive, items):
395 a464ce71 Michael Hanselmann
  """Strict dictionary check with specific keys.
396 a464ce71 Michael Hanselmann

397 a464ce71 Michael Hanselmann
  @type require_all: boolean
398 a464ce71 Michael Hanselmann
  @param require_all: Whether all keys in L{items} are required
399 a464ce71 Michael Hanselmann
  @type exclusive: boolean
400 a464ce71 Michael Hanselmann
  @param exclusive: Whether only keys listed in L{items} should be accepted
401 a464ce71 Michael Hanselmann
  @type items: dictionary
402 a464ce71 Michael Hanselmann
  @param items: Mapping from key (string) to verification function
403 a464ce71 Michael Hanselmann

404 a464ce71 Michael Hanselmann
  """
405 a464ce71 Michael Hanselmann
  descparts = ["Dictionary containing"]
406 a464ce71 Michael Hanselmann
407 a464ce71 Michael Hanselmann
  if exclusive:
408 a464ce71 Michael Hanselmann
    descparts.append(" none but the")
409 a464ce71 Michael Hanselmann
410 a464ce71 Michael Hanselmann
  if require_all:
411 a464ce71 Michael Hanselmann
    descparts.append(" required")
412 a464ce71 Michael Hanselmann
413 a464ce71 Michael Hanselmann
  if len(items) == 1:
414 a464ce71 Michael Hanselmann
    descparts.append(" key ")
415 a464ce71 Michael Hanselmann
  else:
416 a464ce71 Michael Hanselmann
    descparts.append(" keys ")
417 a464ce71 Michael Hanselmann
418 a464ce71 Michael Hanselmann
  descparts.append(utils.CommaJoin("\"%s\" (value %s)" % (key, value)
419 a464ce71 Michael Hanselmann
                                   for (key, value) in items.items()))
420 a464ce71 Michael Hanselmann
421 a464ce71 Michael Hanselmann
  desc = WithDesc("".join(descparts))
422 a464ce71 Michael Hanselmann
423 a464ce71 Michael Hanselmann
  return desc(TAnd(TDict,
424 a464ce71 Michael Hanselmann
                   compat.partial(_TStrictDictCheck, require_all, exclusive,
425 a464ce71 Michael Hanselmann
                                  items)))
426 8620f50e Michael Hanselmann
427 8620f50e Michael Hanselmann
428 8620f50e Michael Hanselmann
def TItems(items):
429 8620f50e Michael Hanselmann
  """Checks individual items of a container.
430 8620f50e Michael Hanselmann

431 8620f50e Michael Hanselmann
  If the verified value and the list of expected items differ in length, this
432 8620f50e Michael Hanselmann
  check considers only as many items as are contained in the shorter list. Use
433 8620f50e Michael Hanselmann
  L{TIsLength} to enforce a certain length.
434 8620f50e Michael Hanselmann

435 8620f50e Michael Hanselmann
  @type items: list
436 8620f50e Michael Hanselmann
  @param items: List of checks
437 8620f50e Michael Hanselmann

438 8620f50e Michael Hanselmann
  """
439 8620f50e Michael Hanselmann
  assert items, "Need items"
440 8620f50e Michael Hanselmann
441 8620f50e Michael Hanselmann
  text = ["Item", "item"]
442 8620f50e Michael Hanselmann
  desc = WithDesc(utils.CommaJoin("%s %s is %s" %
443 8620f50e Michael Hanselmann
                                  (text[int(idx > 0)], idx, Parens(check))
444 8620f50e Michael Hanselmann
                                  for (idx, check) in enumerate(items)))
445 8620f50e Michael Hanselmann
446 8620f50e Michael Hanselmann
  return desc(lambda value: compat.all(check(i)
447 8620f50e Michael Hanselmann
                                       for (check, i) in zip(items, value)))