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