| OLD | NEW |
| (Empty) |
| 1 # -*- test-case-name: twisted.test.test_formmethod -*- | |
| 2 # Copyright (c) 2001-2004 Twisted Matrix Laboratories. | |
| 3 # See LICENSE for details. | |
| 4 | |
| 5 | |
| 6 """ | |
| 7 Form-based method objects. | |
| 8 | |
| 9 This module contains support for descriptive method signatures that can be used | |
| 10 to format methods. Currently this is only used by woven. | |
| 11 """ | |
| 12 | |
| 13 import calendar | |
| 14 | |
| 15 class FormException(Exception): | |
| 16 """An error occurred calling the form method. | |
| 17 """ | |
| 18 def __init__(self, *args, **kwargs): | |
| 19 Exception.__init__(self, *args) | |
| 20 self.descriptions = kwargs | |
| 21 | |
| 22 | |
| 23 class InputError(FormException): | |
| 24 """ | |
| 25 An error occurred with some input. | |
| 26 """ | |
| 27 | |
| 28 | |
| 29 class Argument: | |
| 30 """Base class for form arguments.""" | |
| 31 | |
| 32 # default value for argument, if no other default is given | |
| 33 defaultDefault = None | |
| 34 | |
| 35 def __init__(self, name, default=None, shortDesc=None, | |
| 36 longDesc=None, hints=None, allowNone=1): | |
| 37 self.name = name | |
| 38 self.allowNone = allowNone | |
| 39 if default is None: | |
| 40 default = self.defaultDefault | |
| 41 self.default = default | |
| 42 self.shortDesc = shortDesc | |
| 43 self.longDesc = longDesc | |
| 44 if not hints: | |
| 45 hints = {} | |
| 46 self.hints = hints | |
| 47 | |
| 48 def addHints(self, **kwargs): | |
| 49 self.hints.update(kwargs) | |
| 50 | |
| 51 def getHint(self, name, default=None): | |
| 52 return self.hints.get(name, default) | |
| 53 | |
| 54 def getShortDescription(self): | |
| 55 return self.shortDesc or self.name.capitalize() | |
| 56 | |
| 57 def getLongDescription(self): | |
| 58 return self.longDesc or '' #self.shortDesc or "The %s." % self.name | |
| 59 | |
| 60 def coerce(self, val): | |
| 61 """Convert the value to the correct format.""" | |
| 62 raise NotImplementedError, "implement in subclass" | |
| 63 | |
| 64 | |
| 65 class String(Argument): | |
| 66 """A single string. | |
| 67 """ | |
| 68 defaultDefault = '' | |
| 69 min = 0 | |
| 70 max = None | |
| 71 | |
| 72 def __init__(self, name, default=None, shortDesc=None, | |
| 73 longDesc=None, hints=None, allowNone=1, min=0, max=None): | |
| 74 Argument.__init__(self, name, default=default, shortDesc=shortDesc, | |
| 75 longDesc=longDesc, hints=hints, allowNone=allowNone) | |
| 76 self.min = min | |
| 77 self.max = max | |
| 78 | |
| 79 def coerce(self, val): | |
| 80 s = str(val) | |
| 81 if len(s) < self.min: | |
| 82 raise InputError, "Value must be at least %s characters long" % self
.min | |
| 83 if self.max != None and len(s) > self.max: | |
| 84 raise InputError, "Value must be at most %s characters long" % self.
max | |
| 85 return str(val) | |
| 86 | |
| 87 | |
| 88 class Text(String): | |
| 89 """A long string. | |
| 90 """ | |
| 91 | |
| 92 | |
| 93 class Password(String): | |
| 94 """A string which should be obscured when input. | |
| 95 """ | |
| 96 | |
| 97 | |
| 98 class VerifiedPassword(String): | |
| 99 """A string that should be obscured when input and needs verification.""" | |
| 100 | |
| 101 def coerce(self, vals): | |
| 102 if len(vals) != 2 or vals[0] != vals[1]: | |
| 103 raise InputError, "Please enter the same password twice." | |
| 104 s = str(vals[0]) | |
| 105 if len(s) < self.min: | |
| 106 raise InputError, "Value must be at least %s characters long" % self
.min | |
| 107 if self.max != None and len(s) > self.max: | |
| 108 raise InputError, "Value must be at most %s characters long" % self.
max | |
| 109 return s | |
| 110 | |
| 111 | |
| 112 class Hidden(String): | |
| 113 """A string which is not displayed. | |
| 114 | |
| 115 The passed default is used as the value. | |
| 116 """ | |
| 117 | |
| 118 | |
| 119 class Integer(Argument): | |
| 120 """A single integer. | |
| 121 """ | |
| 122 defaultDefault = None | |
| 123 | |
| 124 def __init__(self, name, allowNone=1, default=None, shortDesc=None, | |
| 125 longDesc=None, hints=None): | |
| 126 #although Argument now has allowNone, that was recently added, and | |
| 127 #putting it at the end kept things which relied on argument order | |
| 128 #from breaking. However, allowNone originally was in here, so | |
| 129 #I have to keep the same order, to prevent breaking code that | |
| 130 #depends on argument order only | |
| 131 Argument.__init__(self, name, default, shortDesc, longDesc, hints, | |
| 132 allowNone) | |
| 133 | |
| 134 def coerce(self, val): | |
| 135 if not val.strip() and self.allowNone: | |
| 136 return None | |
| 137 try: | |
| 138 return int(val) | |
| 139 except ValueError: | |
| 140 raise InputError, "%s is not valid, please enter a whole number, e.g
. 10" % val | |
| 141 | |
| 142 | |
| 143 class IntegerRange(Integer): | |
| 144 | |
| 145 def __init__(self, name, min, max, allowNone=1, default=None, shortDesc=None
, | |
| 146 longDesc=None, hints=None): | |
| 147 self.min = min | |
| 148 self.max = max | |
| 149 Integer.__init__(self, name, allowNone=allowNone, default=default, short
Desc=shortDesc, | |
| 150 longDesc=longDesc, hints=hints) | |
| 151 | |
| 152 def coerce(self, val): | |
| 153 result = Integer.coerce(self, val) | |
| 154 if self.allowNone and result == None: | |
| 155 return result | |
| 156 if result < self.min: | |
| 157 raise InputError, "Value %s is too small, it should be at least %s"
% (result, self.min) | |
| 158 if result > self.max: | |
| 159 raise InputError, "Value %s is too large, it should be at most %s" %
(result, self.max) | |
| 160 return result | |
| 161 | |
| 162 | |
| 163 class Float(Argument): | |
| 164 | |
| 165 defaultDefault = None | |
| 166 | |
| 167 def __init__(self, name, allowNone=1, default=None, shortDesc=None, | |
| 168 longDesc=None, hints=None): | |
| 169 #although Argument now has allowNone, that was recently added, and | |
| 170 #putting it at the end kept things which relied on argument order | |
| 171 #from breaking. However, allowNone originally was in here, so | |
| 172 #I have to keep the same order, to prevent breaking code that | |
| 173 #depends on argument order only | |
| 174 Argument.__init__(self, name, default, shortDesc, longDesc, hints, | |
| 175 allowNone) | |
| 176 | |
| 177 | |
| 178 def coerce(self, val): | |
| 179 if not val.strip() and self.allowNone: | |
| 180 return None | |
| 181 try: | |
| 182 return float(val) | |
| 183 except ValueError: | |
| 184 raise InputError, "Invalid float: %s" % val | |
| 185 | |
| 186 | |
| 187 class Choice(Argument): | |
| 188 """ | |
| 189 The result of a choice between enumerated types. The choices should | |
| 190 be a list of tuples of tag, value, and description. The tag will be | |
| 191 the value returned if the user hits "Submit", and the description | |
| 192 is the bale for the enumerated type. default is a list of all the | |
| 193 values (seconds element in choices). If no defaults are specified, | |
| 194 initially the first item will be selected. Only one item can (should) | |
| 195 be selected at once. | |
| 196 """ | |
| 197 def __init__(self, name, choices=[], default=[], shortDesc=None, | |
| 198 longDesc=None, hints=None, allowNone=1): | |
| 199 self.choices = choices | |
| 200 if choices and not default: | |
| 201 default.append(choices[0][1]) | |
| 202 Argument.__init__(self, name, default, shortDesc, longDesc, hints, allow
None=allowNone) | |
| 203 | |
| 204 def coerce(self, inIdent): | |
| 205 for ident, val, desc in self.choices: | |
| 206 if ident == inIdent: | |
| 207 return val | |
| 208 else: | |
| 209 raise InputError("Invalid Choice: %s" % inIdent) | |
| 210 | |
| 211 | |
| 212 class Flags(Argument): | |
| 213 """ | |
| 214 The result of a checkbox group or multi-menu. The flags should be a | |
| 215 list of tuples of tag, value, and description. The tag will be | |
| 216 the value returned if the user hits "Submit", and the description | |
| 217 is the bale for the enumerated type. default is a list of all the | |
| 218 values (second elements in flags). If no defaults are specified, | |
| 219 initially nothing will be selected. Several items may be selected at | |
| 220 once. | |
| 221 """ | |
| 222 def __init__(self, name, flags=(), default=(), shortDesc=None, | |
| 223 longDesc=None, hints=None, allowNone=1): | |
| 224 self.flags = flags | |
| 225 Argument.__init__(self, name, default, shortDesc, longDesc, hints, allow
None=allowNone) | |
| 226 | |
| 227 def coerce(self, inFlagKeys): | |
| 228 if not inFlagKeys: | |
| 229 return [] | |
| 230 outFlags = [] | |
| 231 for inFlagKey in inFlagKeys: | |
| 232 for flagKey, flagVal, flagDesc in self.flags: | |
| 233 if inFlagKey == flagKey: | |
| 234 outFlags.append(flagVal) | |
| 235 break | |
| 236 else: | |
| 237 raise InputError("Invalid Flag: %s" % inFlagKey) | |
| 238 return outFlags | |
| 239 | |
| 240 | |
| 241 class CheckGroup(Flags): | |
| 242 pass | |
| 243 | |
| 244 | |
| 245 class RadioGroup(Choice): | |
| 246 pass | |
| 247 | |
| 248 | |
| 249 class Boolean(Argument): | |
| 250 def coerce(self, inVal): | |
| 251 if not inVal: | |
| 252 return 0 | |
| 253 lInVal = str(inVal).lower() | |
| 254 if lInVal in ('no', 'n', 'f', 'false', '0'): | |
| 255 return 0 | |
| 256 return 1 | |
| 257 | |
| 258 class File(Argument): | |
| 259 def __init__(self, name, allowNone=1, shortDesc=None, longDesc=None, | |
| 260 hints=None): | |
| 261 self.allowNone = allowNone | |
| 262 Argument.__init__(self, name, None, shortDesc, longDesc, hints) | |
| 263 | |
| 264 def coerce(self, file): | |
| 265 if not file and self.allowNone: | |
| 266 return None | |
| 267 elif file: | |
| 268 return file | |
| 269 else: | |
| 270 raise InputError, "Invalid File" | |
| 271 | |
| 272 def positiveInt(x): | |
| 273 x = int(x) | |
| 274 if x <= 0: raise ValueError | |
| 275 return x | |
| 276 | |
| 277 class Date(Argument): | |
| 278 """A date -- (year, month, day) tuple.""" | |
| 279 | |
| 280 defaultDefault = None | |
| 281 | |
| 282 def __init__(self, name, allowNone=1, default=None, shortDesc=None, | |
| 283 longDesc=None, hints=None): | |
| 284 Argument.__init__(self, name, default, shortDesc, longDesc, hints) | |
| 285 self.allowNone = allowNone | |
| 286 if not allowNone: | |
| 287 self.defaultDefault = (1970, 1, 1) | |
| 288 | |
| 289 def coerce(self, args): | |
| 290 """Return tuple of ints (year, month, day).""" | |
| 291 if tuple(args) == ("", "", "") and self.allowNone: | |
| 292 return None | |
| 293 | |
| 294 try: | |
| 295 year, month, day = map(positiveInt, args) | |
| 296 except ValueError: | |
| 297 raise InputError, "Invalid date" | |
| 298 if (month, day) == (2, 29): | |
| 299 if not calendar.isleap(year): | |
| 300 raise InputError, "%d was not a leap year" % year | |
| 301 else: | |
| 302 return year, month, day | |
| 303 try: | |
| 304 mdays = calendar.mdays[month] | |
| 305 except IndexError: | |
| 306 raise InputError, "Invalid date" | |
| 307 if day > mdays: | |
| 308 raise InputError, "Invalid date" | |
| 309 return year, month, day | |
| 310 | |
| 311 | |
| 312 class Submit(Choice): | |
| 313 """Submit button or a reasonable facsimile thereof.""" | |
| 314 | |
| 315 def __init__(self, name, choices=[("Submit", "submit", "Submit form")], | |
| 316 reset=0, shortDesc=None, longDesc=None, allowNone=0, hints=None
): | |
| 317 Choice.__init__(self, name, choices=choices, shortDesc=shortDesc, | |
| 318 longDesc=longDesc, hints=hints) | |
| 319 self.allowNone = allowNone | |
| 320 self.reset = reset | |
| 321 | |
| 322 def coerce(self, value): | |
| 323 if self.allowNone and not value: | |
| 324 return None | |
| 325 else: | |
| 326 return Choice.coerce(self, value) | |
| 327 | |
| 328 | |
| 329 class PresentationHint: | |
| 330 """ | |
| 331 A hint to a particular system. | |
| 332 """ | |
| 333 | |
| 334 | |
| 335 class MethodSignature: | |
| 336 | |
| 337 def __init__(self, *sigList): | |
| 338 """ | |
| 339 """ | |
| 340 self.methodSignature = sigList | |
| 341 | |
| 342 def getArgument(self, name): | |
| 343 for a in self.methodSignature: | |
| 344 if a.name == name: | |
| 345 return a | |
| 346 | |
| 347 def method(self, callable, takesRequest=False): | |
| 348 return FormMethod(self, callable, takesRequest) | |
| 349 | |
| 350 | |
| 351 class FormMethod: | |
| 352 """A callable object with a signature.""" | |
| 353 | |
| 354 def __init__(self, signature, callable, takesRequest=False): | |
| 355 self.signature = signature | |
| 356 self.callable = callable | |
| 357 self.takesRequest = takesRequest | |
| 358 | |
| 359 def getArgs(self): | |
| 360 return tuple(self.signature.methodSignature) | |
| 361 | |
| 362 def call(self,*args,**kw): | |
| 363 return self.callable(*args,**kw) | |
| OLD | NEW |