Statistics
| Branch: | Tag: | Revision:

root / lib / ht.py @ 08f8c82c

History | View | Annotate | Download (8.8 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 7fc548e9 Michael Hanselmann
class _DescWrapper(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 7fc548e9 Michael Hanselmann
    self._text = text
63 7fc548e9 Michael Hanselmann
    self._fn = fn
64 7fc548e9 Michael Hanselmann
65 7fc548e9 Michael Hanselmann
  def __call__(self, *args):
66 7fc548e9 Michael Hanselmann
    return self._fn(*args)
67 7fc548e9 Michael Hanselmann
68 7fc548e9 Michael Hanselmann
  def __str__(self):
69 7fc548e9 Michael Hanselmann
    return self._text
70 7fc548e9 Michael Hanselmann
71 7fc548e9 Michael Hanselmann
72 8c9ee749 Michael Hanselmann
def WithDesc(text):
73 8c9ee749 Michael Hanselmann
  """Builds wrapper class with description text.
74 8c9ee749 Michael Hanselmann

75 8c9ee749 Michael Hanselmann
  @type text: string
76 8c9ee749 Michael Hanselmann
  @param text: Description text
77 8c9ee749 Michael Hanselmann
  @return: Callable class
78 8c9ee749 Michael Hanselmann

79 8c9ee749 Michael Hanselmann
  """
80 8c9ee749 Michael Hanselmann
  assert text[0] == text[0].upper()
81 8c9ee749 Michael Hanselmann
82 7fc548e9 Michael Hanselmann
  return compat.partial(_DescWrapper, text)
83 8c9ee749 Michael Hanselmann
84 8c9ee749 Michael Hanselmann
85 8c9ee749 Michael Hanselmann
def CombinationDesc(op, args, fn):
86 8c9ee749 Michael Hanselmann
  """Build description for combinating operator.
87 8c9ee749 Michael Hanselmann

88 8c9ee749 Michael Hanselmann
  @type op: string
89 8c9ee749 Michael Hanselmann
  @param op: Operator as text (e.g. "and")
90 8c9ee749 Michael Hanselmann
  @type args: list
91 8c9ee749 Michael Hanselmann
  @param args: Operator arguments
92 8c9ee749 Michael Hanselmann
  @type fn: callable
93 8c9ee749 Michael Hanselmann
  @param fn: Wrapped function
94 8c9ee749 Michael Hanselmann

95 8c9ee749 Michael Hanselmann
  """
96 8c9ee749 Michael Hanselmann
  if len(args) == 1:
97 8c9ee749 Michael Hanselmann
    descr = str(args[0])
98 8c9ee749 Michael Hanselmann
  else:
99 8c9ee749 Michael Hanselmann
    descr = (" %s " % op).join(Parens(i) for i in args)
100 8c9ee749 Michael Hanselmann
101 8c9ee749 Michael Hanselmann
  return WithDesc(descr)(fn)
102 8c9ee749 Michael Hanselmann
103 62e0e880 Iustin Pop
104 62e0e880 Iustin Pop
# Modifiable default values; need to define these here before the
105 62e0e880 Iustin Pop
# actual LUs
106 62e0e880 Iustin Pop
107 8c9ee749 Michael Hanselmann
@WithDesc(str([]))
108 62e0e880 Iustin Pop
def EmptyList():
109 62e0e880 Iustin Pop
  """Returns an empty list.
110 62e0e880 Iustin Pop

111 62e0e880 Iustin Pop
  """
112 62e0e880 Iustin Pop
  return []
113 62e0e880 Iustin Pop
114 62e0e880 Iustin Pop
115 8c9ee749 Michael Hanselmann
@WithDesc(str({}))
116 62e0e880 Iustin Pop
def EmptyDict():
117 62e0e880 Iustin Pop
  """Returns an empty dict.
118 62e0e880 Iustin Pop

119 62e0e880 Iustin Pop
  """
120 62e0e880 Iustin Pop
  return {}
121 62e0e880 Iustin Pop
122 62e0e880 Iustin Pop
123 62e0e880 Iustin Pop
#: The without-default default value
124 62e0e880 Iustin Pop
NoDefault = object()
125 62e0e880 Iustin Pop
126 62e0e880 Iustin Pop
127 8c9ee749 Michael Hanselmann
#: The no-type (value too complex to check it in the type system)
128 62e0e880 Iustin Pop
NoType = object()
129 62e0e880 Iustin Pop
130 62e0e880 Iustin Pop
131 62e0e880 Iustin Pop
# Some basic types
132 8620f50e Michael Hanselmann
@WithDesc("Anything")
133 8620f50e Michael Hanselmann
def TAny(_):
134 8620f50e Michael Hanselmann
  """Accepts any value.
135 8620f50e Michael Hanselmann

136 8620f50e Michael Hanselmann
  """
137 8620f50e Michael Hanselmann
  return True
138 8620f50e Michael Hanselmann
139 8620f50e Michael Hanselmann
140 8c9ee749 Michael Hanselmann
@WithDesc("NotNone")
141 62e0e880 Iustin Pop
def TNotNone(val):
142 62e0e880 Iustin Pop
  """Checks if the given value is not None.
143 62e0e880 Iustin Pop

144 62e0e880 Iustin Pop
  """
145 62e0e880 Iustin Pop
  return val is not None
146 62e0e880 Iustin Pop
147 62e0e880 Iustin Pop
148 8c9ee749 Michael Hanselmann
@WithDesc("None")
149 62e0e880 Iustin Pop
def TNone(val):
150 62e0e880 Iustin Pop
  """Checks if the given value is None.
151 62e0e880 Iustin Pop

152 62e0e880 Iustin Pop
  """
153 62e0e880 Iustin Pop
  return val is None
154 62e0e880 Iustin Pop
155 62e0e880 Iustin Pop
156 8c9ee749 Michael Hanselmann
@WithDesc("Boolean")
157 62e0e880 Iustin Pop
def TBool(val):
158 62e0e880 Iustin Pop
  """Checks if the given value is a boolean.
159 62e0e880 Iustin Pop

160 62e0e880 Iustin Pop
  """
161 62e0e880 Iustin Pop
  return isinstance(val, bool)
162 62e0e880 Iustin Pop
163 62e0e880 Iustin Pop
164 8c9ee749 Michael Hanselmann
@WithDesc("Integer")
165 62e0e880 Iustin Pop
def TInt(val):
166 62e0e880 Iustin Pop
  """Checks if the given value is an integer.
167 62e0e880 Iustin Pop

168 62e0e880 Iustin Pop
  """
169 8568de9e Michael Hanselmann
  # For backwards compatibility with older Python versions, boolean values are
170 8568de9e Michael Hanselmann
  # also integers and should be excluded in this test.
171 8568de9e Michael Hanselmann
  #
172 8568de9e Michael Hanselmann
  # >>> (isinstance(False, int), isinstance(True, int))
173 8568de9e Michael Hanselmann
  # (True, True)
174 b99b607f Michael Hanselmann
  return isinstance(val, (int, long)) and not isinstance(val, bool)
175 62e0e880 Iustin Pop
176 62e0e880 Iustin Pop
177 8c9ee749 Michael Hanselmann
@WithDesc("Float")
178 62e0e880 Iustin Pop
def TFloat(val):
179 62e0e880 Iustin Pop
  """Checks if the given value is a float.
180 62e0e880 Iustin Pop

181 62e0e880 Iustin Pop
  """
182 62e0e880 Iustin Pop
  return isinstance(val, float)
183 62e0e880 Iustin Pop
184 62e0e880 Iustin Pop
185 8c9ee749 Michael Hanselmann
@WithDesc("String")
186 62e0e880 Iustin Pop
def TString(val):
187 62e0e880 Iustin Pop
  """Checks if the given value is a string.
188 62e0e880 Iustin Pop

189 62e0e880 Iustin Pop
  """
190 62e0e880 Iustin Pop
  return isinstance(val, basestring)
191 62e0e880 Iustin Pop
192 62e0e880 Iustin Pop
193 8c9ee749 Michael Hanselmann
@WithDesc("EvalToTrue")
194 62e0e880 Iustin Pop
def TTrue(val):
195 62e0e880 Iustin Pop
  """Checks if a given value evaluates to a boolean True value.
196 62e0e880 Iustin Pop

197 62e0e880 Iustin Pop
  """
198 62e0e880 Iustin Pop
  return bool(val)
199 62e0e880 Iustin Pop
200 62e0e880 Iustin Pop
201 62e0e880 Iustin Pop
def TElemOf(target_list):
202 62e0e880 Iustin Pop
  """Builds a function that checks if a given value is a member of a list.
203 62e0e880 Iustin Pop

204 62e0e880 Iustin Pop
  """
205 8c9ee749 Michael Hanselmann
  def fn(val):
206 8c9ee749 Michael Hanselmann
    return val in target_list
207 8c9ee749 Michael Hanselmann
208 8c9ee749 Michael Hanselmann
  return WithDesc("OneOf %s" % (utils.CommaJoin(target_list), ))(fn)
209 62e0e880 Iustin Pop
210 62e0e880 Iustin Pop
211 62e0e880 Iustin Pop
# Container types
212 8c9ee749 Michael Hanselmann
@WithDesc("List")
213 62e0e880 Iustin Pop
def TList(val):
214 62e0e880 Iustin Pop
  """Checks if the given value is a list.
215 62e0e880 Iustin Pop

216 62e0e880 Iustin Pop
  """
217 62e0e880 Iustin Pop
  return isinstance(val, list)
218 62e0e880 Iustin Pop
219 62e0e880 Iustin Pop
220 8c9ee749 Michael Hanselmann
@WithDesc("Dictionary")
221 62e0e880 Iustin Pop
def TDict(val):
222 62e0e880 Iustin Pop
  """Checks if the given value is a dictionary.
223 62e0e880 Iustin Pop

224 62e0e880 Iustin Pop
  """
225 62e0e880 Iustin Pop
  return isinstance(val, dict)
226 62e0e880 Iustin Pop
227 62e0e880 Iustin Pop
228 62e0e880 Iustin Pop
def TIsLength(size):
229 62e0e880 Iustin Pop
  """Check is the given container is of the given size.
230 62e0e880 Iustin Pop

231 62e0e880 Iustin Pop
  """
232 8c9ee749 Michael Hanselmann
  def fn(container):
233 8c9ee749 Michael Hanselmann
    return len(container) == size
234 8c9ee749 Michael Hanselmann
235 8c9ee749 Michael Hanselmann
  return WithDesc("Length %s" % (size, ))(fn)
236 62e0e880 Iustin Pop
237 62e0e880 Iustin Pop
238 62e0e880 Iustin Pop
# Combinator types
239 62e0e880 Iustin Pop
def TAnd(*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.all(t(val) for t in args)
245 8c9ee749 Michael Hanselmann
246 8c9ee749 Michael Hanselmann
  return CombinationDesc("and", args, fn)
247 62e0e880 Iustin Pop
248 62e0e880 Iustin Pop
249 62e0e880 Iustin Pop
def TOr(*args):
250 62e0e880 Iustin Pop
  """Combine multiple functions using an AND operation.
251 62e0e880 Iustin Pop

252 62e0e880 Iustin Pop
  """
253 62e0e880 Iustin Pop
  def fn(val):
254 62e0e880 Iustin Pop
    return compat.any(t(val) for t in args)
255 8c9ee749 Michael Hanselmann
256 8c9ee749 Michael Hanselmann
  return CombinationDesc("or", args, fn)
257 62e0e880 Iustin Pop
258 62e0e880 Iustin Pop
259 62e0e880 Iustin Pop
def TMap(fn, test):
260 62e0e880 Iustin Pop
  """Checks that a modified version of the argument passes the given test.
261 62e0e880 Iustin Pop

262 62e0e880 Iustin Pop
  """
263 8c9ee749 Michael Hanselmann
  return WithDesc("Result of %s must be %s" %
264 8c9ee749 Michael Hanselmann
                  (Parens(fn), Parens(test)))(lambda val: test(fn(val)))
265 62e0e880 Iustin Pop
266 62e0e880 Iustin Pop
267 8620f50e Michael Hanselmann
def TRegex(pobj):
268 8620f50e Michael Hanselmann
  """Checks whether a string matches a specific regular expression.
269 8620f50e Michael Hanselmann

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

272 8620f50e Michael Hanselmann
  """
273 8620f50e Michael Hanselmann
  desc = WithDesc("String matching regex \"%s\"" %
274 8620f50e Michael Hanselmann
                  pobj.pattern.encode("string_escape"))
275 8620f50e Michael Hanselmann
276 8620f50e Michael Hanselmann
  return desc(TAnd(TString, pobj.match))
277 8620f50e Michael Hanselmann
278 8620f50e Michael Hanselmann
279 62e0e880 Iustin Pop
# Type aliases
280 62e0e880 Iustin Pop
281 62e0e880 Iustin Pop
#: a non-empty string
282 8c9ee749 Michael Hanselmann
TNonEmptyString = WithDesc("NonEmptyString")(TAnd(TString, TTrue))
283 62e0e880 Iustin Pop
284 62e0e880 Iustin Pop
#: a maybe non-empty string
285 62e0e880 Iustin Pop
TMaybeString = TOr(TNonEmptyString, TNone)
286 62e0e880 Iustin Pop
287 62e0e880 Iustin Pop
#: a maybe boolean (bool or none)
288 62e0e880 Iustin Pop
TMaybeBool = TOr(TBool, TNone)
289 62e0e880 Iustin Pop
290 5f074973 Michael Hanselmann
#: Maybe a dictionary (dict or None)
291 5f074973 Michael Hanselmann
TMaybeDict = TOr(TDict, TNone)
292 62e0e880 Iustin Pop
293 62e0e880 Iustin Pop
#: a positive integer
294 8c9ee749 Michael Hanselmann
TPositiveInt = \
295 8c9ee749 Michael Hanselmann
  TAnd(TInt, WithDesc("EqualGreaterZero")(lambda v: v >= 0))
296 62e0e880 Iustin Pop
297 62e0e880 Iustin Pop
#: a strictly positive integer
298 8c9ee749 Michael Hanselmann
TStrictPositiveInt = \
299 8c9ee749 Michael Hanselmann
  TAnd(TInt, WithDesc("GreaterThanZero")(lambda v: v > 0))
300 62e0e880 Iustin Pop
301 b247c6fc Michael Hanselmann
#: a strictly negative integer (0 > value)
302 b247c6fc Michael Hanselmann
TStrictNegativeInt = \
303 b247c6fc Michael Hanselmann
  TAnd(TInt, WithDesc("LessThanZero")(compat.partial(operator.gt, 0)))
304 b247c6fc Michael Hanselmann
305 beff3779 René Nussbaumer
#: a positive float
306 beff3779 René Nussbaumer
TPositiveFloat = \
307 beff3779 René Nussbaumer
  TAnd(TFloat, WithDesc("EqualGreaterZero")(lambda v: v >= 0.0))
308 beff3779 René Nussbaumer
309 8620f50e Michael Hanselmann
#: Job ID
310 8620f50e Michael Hanselmann
TJobId = TOr(TPositiveInt,
311 8620f50e Michael Hanselmann
             TRegex(re.compile("^%s$" % constants.JOB_ID_TEMPLATE)))
312 8620f50e Michael Hanselmann
313 697f49d5 Michael Hanselmann
#: Number
314 697f49d5 Michael Hanselmann
TNumber = TOr(TInt, TFloat)
315 697f49d5 Michael Hanselmann
316 b247c6fc Michael Hanselmann
#: Relative job ID
317 b247c6fc Michael Hanselmann
TRelativeJobId = WithDesc("RelativeJobId")(TStrictNegativeInt)
318 b247c6fc Michael Hanselmann
319 62e0e880 Iustin Pop
320 62e0e880 Iustin Pop
def TListOf(my_type):
321 62e0e880 Iustin Pop
  """Checks if a given value is a list with all elements of the same type.
322 62e0e880 Iustin Pop

323 62e0e880 Iustin Pop
  """
324 8c9ee749 Michael Hanselmann
  desc = WithDesc("List of %s" % (Parens(my_type), ))
325 8c9ee749 Michael Hanselmann
  return desc(TAnd(TList, lambda lst: compat.all(my_type(v) for v in lst)))
326 62e0e880 Iustin Pop
327 62e0e880 Iustin Pop
328 62e0e880 Iustin Pop
def TDictOf(key_type, val_type):
329 62e0e880 Iustin Pop
  """Checks a dict type for the type of its key/values.
330 62e0e880 Iustin Pop

331 62e0e880 Iustin Pop
  """
332 8c9ee749 Michael Hanselmann
  desc = WithDesc("Dictionary with keys of %s and values of %s" %
333 8c9ee749 Michael Hanselmann
                  (Parens(key_type), Parens(val_type)))
334 8c9ee749 Michael Hanselmann
335 8c9ee749 Michael Hanselmann
  def fn(container):
336 8c9ee749 Michael Hanselmann
    return (compat.all(key_type(v) for v in container.keys()) and
337 8c9ee749 Michael Hanselmann
            compat.all(val_type(v) for v in container.values()))
338 8c9ee749 Michael Hanselmann
339 8c9ee749 Michael Hanselmann
  return desc(TAnd(TDict, fn))
340 a464ce71 Michael Hanselmann
341 a464ce71 Michael Hanselmann
342 a464ce71 Michael Hanselmann
def _TStrictDictCheck(require_all, exclusive, items, val):
343 a464ce71 Michael Hanselmann
  """Helper function for L{TStrictDict}.
344 a464ce71 Michael Hanselmann

345 a464ce71 Michael Hanselmann
  """
346 a464ce71 Michael Hanselmann
  notfound_fn = lambda _: not exclusive
347 a464ce71 Michael Hanselmann
348 a464ce71 Michael Hanselmann
  if require_all and not frozenset(val.keys()).issuperset(items.keys()):
349 a464ce71 Michael Hanselmann
    # Requires items not found in value
350 a464ce71 Michael Hanselmann
    return False
351 a464ce71 Michael Hanselmann
352 a464ce71 Michael Hanselmann
  return compat.all(items.get(key, notfound_fn)(value)
353 a464ce71 Michael Hanselmann
                    for (key, value) in val.items())
354 a464ce71 Michael Hanselmann
355 a464ce71 Michael Hanselmann
356 a464ce71 Michael Hanselmann
def TStrictDict(require_all, exclusive, items):
357 a464ce71 Michael Hanselmann
  """Strict dictionary check with specific keys.
358 a464ce71 Michael Hanselmann

359 a464ce71 Michael Hanselmann
  @type require_all: boolean
360 a464ce71 Michael Hanselmann
  @param require_all: Whether all keys in L{items} are required
361 a464ce71 Michael Hanselmann
  @type exclusive: boolean
362 a464ce71 Michael Hanselmann
  @param exclusive: Whether only keys listed in L{items} should be accepted
363 a464ce71 Michael Hanselmann
  @type items: dictionary
364 a464ce71 Michael Hanselmann
  @param items: Mapping from key (string) to verification function
365 a464ce71 Michael Hanselmann

366 a464ce71 Michael Hanselmann
  """
367 a464ce71 Michael Hanselmann
  descparts = ["Dictionary containing"]
368 a464ce71 Michael Hanselmann
369 a464ce71 Michael Hanselmann
  if exclusive:
370 a464ce71 Michael Hanselmann
    descparts.append(" none but the")
371 a464ce71 Michael Hanselmann
372 a464ce71 Michael Hanselmann
  if require_all:
373 a464ce71 Michael Hanselmann
    descparts.append(" required")
374 a464ce71 Michael Hanselmann
375 a464ce71 Michael Hanselmann
  if len(items) == 1:
376 a464ce71 Michael Hanselmann
    descparts.append(" key ")
377 a464ce71 Michael Hanselmann
  else:
378 a464ce71 Michael Hanselmann
    descparts.append(" keys ")
379 a464ce71 Michael Hanselmann
380 a464ce71 Michael Hanselmann
  descparts.append(utils.CommaJoin("\"%s\" (value %s)" % (key, value)
381 a464ce71 Michael Hanselmann
                                   for (key, value) in items.items()))
382 a464ce71 Michael Hanselmann
383 a464ce71 Michael Hanselmann
  desc = WithDesc("".join(descparts))
384 a464ce71 Michael Hanselmann
385 a464ce71 Michael Hanselmann
  return desc(TAnd(TDict,
386 a464ce71 Michael Hanselmann
                   compat.partial(_TStrictDictCheck, require_all, exclusive,
387 a464ce71 Michael Hanselmann
                                  items)))
388 8620f50e Michael Hanselmann
389 8620f50e Michael Hanselmann
390 8620f50e Michael Hanselmann
def TItems(items):
391 8620f50e Michael Hanselmann
  """Checks individual items of a container.
392 8620f50e Michael Hanselmann

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

397 8620f50e Michael Hanselmann
  @type items: list
398 8620f50e Michael Hanselmann
  @param items: List of checks
399 8620f50e Michael Hanselmann

400 8620f50e Michael Hanselmann
  """
401 8620f50e Michael Hanselmann
  assert items, "Need items"
402 8620f50e Michael Hanselmann
403 8620f50e Michael Hanselmann
  text = ["Item", "item"]
404 8620f50e Michael Hanselmann
  desc = WithDesc(utils.CommaJoin("%s %s is %s" %
405 8620f50e Michael Hanselmann
                                  (text[int(idx > 0)], idx, Parens(check))
406 8620f50e Michael Hanselmann
                                  for (idx, check) in enumerate(items)))
407 8620f50e Michael Hanselmann
408 8620f50e Michael Hanselmann
  return desc(lambda value: compat.all(check(i)
409 8620f50e Michael Hanselmann
                                       for (check, i) in zip(items, value)))