Statistics
| Branch: | Tag: | Revision:

root / lib / ht.py @ 98dfcaff

History | View | Annotate | Download (9.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 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 62e0e880 Iustin Pop
#: a strictly positive integer
326 8c9ee749 Michael Hanselmann
TStrictPositiveInt = \
327 8c9ee749 Michael Hanselmann
  TAnd(TInt, WithDesc("GreaterThanZero")(lambda v: v > 0))
328 62e0e880 Iustin Pop
329 b247c6fc Michael Hanselmann
#: a strictly negative integer (0 > value)
330 b247c6fc Michael Hanselmann
TStrictNegativeInt = \
331 b247c6fc Michael Hanselmann
  TAnd(TInt, WithDesc("LessThanZero")(compat.partial(operator.gt, 0)))
332 b247c6fc Michael Hanselmann
333 beff3779 René Nussbaumer
#: a positive float
334 beff3779 René Nussbaumer
TPositiveFloat = \
335 beff3779 René Nussbaumer
  TAnd(TFloat, WithDesc("EqualGreaterZero")(lambda v: v >= 0.0))
336 beff3779 René Nussbaumer
337 8620f50e Michael Hanselmann
#: Job ID
338 bdfd7802 Michael Hanselmann
TJobId = WithDesc("JobId")(TOr(TPositiveInt,
339 bdfd7802 Michael Hanselmann
                               TRegex(re.compile("^%s$" %
340 bdfd7802 Michael Hanselmann
                                                 constants.JOB_ID_TEMPLATE))))
341 8620f50e Michael Hanselmann
342 697f49d5 Michael Hanselmann
#: Number
343 697f49d5 Michael Hanselmann
TNumber = TOr(TInt, TFloat)
344 697f49d5 Michael Hanselmann
345 b247c6fc Michael Hanselmann
#: Relative job ID
346 b247c6fc Michael Hanselmann
TRelativeJobId = WithDesc("RelativeJobId")(TStrictNegativeInt)
347 b247c6fc Michael Hanselmann
348 62e0e880 Iustin Pop
349 62e0e880 Iustin Pop
def TListOf(my_type):
350 62e0e880 Iustin Pop
  """Checks if a given value is a list with all elements of the same type.
351 62e0e880 Iustin Pop

352 62e0e880 Iustin Pop
  """
353 8c9ee749 Michael Hanselmann
  desc = WithDesc("List of %s" % (Parens(my_type), ))
354 8c9ee749 Michael Hanselmann
  return desc(TAnd(TList, lambda lst: compat.all(my_type(v) for v in lst)))
355 62e0e880 Iustin Pop
356 62e0e880 Iustin Pop
357 62e0e880 Iustin Pop
def TDictOf(key_type, val_type):
358 62e0e880 Iustin Pop
  """Checks a dict type for the type of its key/values.
359 62e0e880 Iustin Pop

360 62e0e880 Iustin Pop
  """
361 8c9ee749 Michael Hanselmann
  desc = WithDesc("Dictionary with keys of %s and values of %s" %
362 8c9ee749 Michael Hanselmann
                  (Parens(key_type), Parens(val_type)))
363 8c9ee749 Michael Hanselmann
364 8c9ee749 Michael Hanselmann
  def fn(container):
365 8c9ee749 Michael Hanselmann
    return (compat.all(key_type(v) for v in container.keys()) and
366 8c9ee749 Michael Hanselmann
            compat.all(val_type(v) for v in container.values()))
367 8c9ee749 Michael Hanselmann
368 8c9ee749 Michael Hanselmann
  return desc(TAnd(TDict, fn))
369 a464ce71 Michael Hanselmann
370 a464ce71 Michael Hanselmann
371 a464ce71 Michael Hanselmann
def _TStrictDictCheck(require_all, exclusive, items, val):
372 a464ce71 Michael Hanselmann
  """Helper function for L{TStrictDict}.
373 a464ce71 Michael Hanselmann

374 a464ce71 Michael Hanselmann
  """
375 a464ce71 Michael Hanselmann
  notfound_fn = lambda _: not exclusive
376 a464ce71 Michael Hanselmann
377 a464ce71 Michael Hanselmann
  if require_all and not frozenset(val.keys()).issuperset(items.keys()):
378 a464ce71 Michael Hanselmann
    # Requires items not found in value
379 a464ce71 Michael Hanselmann
    return False
380 a464ce71 Michael Hanselmann
381 a464ce71 Michael Hanselmann
  return compat.all(items.get(key, notfound_fn)(value)
382 a464ce71 Michael Hanselmann
                    for (key, value) in val.items())
383 a464ce71 Michael Hanselmann
384 a464ce71 Michael Hanselmann
385 a464ce71 Michael Hanselmann
def TStrictDict(require_all, exclusive, items):
386 a464ce71 Michael Hanselmann
  """Strict dictionary check with specific keys.
387 a464ce71 Michael Hanselmann

388 a464ce71 Michael Hanselmann
  @type require_all: boolean
389 a464ce71 Michael Hanselmann
  @param require_all: Whether all keys in L{items} are required
390 a464ce71 Michael Hanselmann
  @type exclusive: boolean
391 a464ce71 Michael Hanselmann
  @param exclusive: Whether only keys listed in L{items} should be accepted
392 a464ce71 Michael Hanselmann
  @type items: dictionary
393 a464ce71 Michael Hanselmann
  @param items: Mapping from key (string) to verification function
394 a464ce71 Michael Hanselmann

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

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

426 8620f50e Michael Hanselmann
  @type items: list
427 8620f50e Michael Hanselmann
  @param items: List of checks
428 8620f50e Michael Hanselmann

429 8620f50e Michael Hanselmann
  """
430 8620f50e Michael Hanselmann
  assert items, "Need items"
431 8620f50e Michael Hanselmann
432 8620f50e Michael Hanselmann
  text = ["Item", "item"]
433 8620f50e Michael Hanselmann
  desc = WithDesc(utils.CommaJoin("%s %s is %s" %
434 8620f50e Michael Hanselmann
                                  (text[int(idx > 0)], idx, Parens(check))
435 8620f50e Michael Hanselmann
                                  for (idx, check) in enumerate(items)))
436 8620f50e Michael Hanselmann
437 8620f50e Michael Hanselmann
  return desc(lambda value: compat.all(check(i)
438 8620f50e Michael Hanselmann
                                       for (check, i) in zip(items, value)))