root / trunk / twisted / python / formmethod.py

Revision 25924, 11.1 kB (checked in by exarkun, 6 months ago)

Merge remove-woven-1522-2

Author: exarkun
Reviewer: therve
Fixes: #1522

Remove twisted.web.woven and all remaining code which used anything from
that package as well as the documentation for Woven.

Line 
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.
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, shortDesc=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, allowNone=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, allowNone=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)
Note: See TracBrowser for help on using the browser.