| Home | Trees | Indices | Help |
|---|
|
|
1 #
2 #
3 #
4 #
5 #
6 #class Constant(FancyValidator):
7 # """
8 # This converter converts everything to the same thing.
9 #
10 # I.e., you pass in the constant value when initializing, then all
11 # values get converted to that constant value.
12 #
13 # This is only really useful for funny situations, like::
14 #
15 # fromEmailValidator = ValidateAny(
16 # ValidEmailAddress(),
17 # Constant('unknown@localhost'))
18 #
19 # In this case, the if the email is not valid
20 # ``'unknown@localhost'`` will be used instead. Of course, you
21 # could use ``if_invalid`` instead.
22 #
23 # Examples::
24 #
25 # >>> Constant('X').to_python('y')
26 # 'X'
27 # """
28 #
29 # __unpackargs__ = ('value',)
30 #
31 # def _to_python(self, value, state):
32 # return self.value
33 #
34 # _from_python = _to_python
35 #
36 #
37 #############################################################
38 ### Normal validators
39 #############################################################
40 #
41 #
42 #
43 #
44 #
45 #
46 #
47 #class OneOf(FancyValidator):
48 # """
49 # Tests that the value is one of the members of a given list.
50 #
51 # If ``testValueList=True``, then if the input value is a list or
52 # tuple, all the members of the sequence will be checked (i.e., the
53 # input must be a subset of the allowed values).
54 #
55 # Use ``hideList=True`` to keep the list of valid values out of the
56 # error message in exceptions.
57 #
58 # Examples::
59 #
60 # >>> oneof = OneOf([1, 2, 3])
61 # >>> oneof.to_python(1)
62 # 1
63 # >>> oneof.to_python(4)
64 # Traceback (most recent call last):
65 # ...
66 # Invalid: Value must be one of: 1; 2; 3 (not 4)
67 # >>> oneof(testValueList=True).to_python([2, 3, [1, 2, 3]])
68 # [2, 3, [1, 2, 3]]
69 # >>> oneof.to_python([2, 3, [1, 2, 3]])
70 # Traceback (most recent call last):
71 # ...
72 # Invalid: Value must be one of: 1; 2; 3 (not [2, 3, [1, 2, 3]])
73 # """
74 #
75 # list = None
76 # testValueList = False
77 # hideList = False
78 #
79 # __unpackargs__ = ('list',)
80 #
81 # messages = dict(
82 # invalid=_('Invalid value'),
83 # notIn=_('Value must be one of: %(items)s (not %(value)r)'))
84 #
85 # def validate_python(self, value, state):
86 # if self.testValueList and isinstance(value, (list, tuple)):
87 # for v in value:
88 # self.validate_python(v, state)
89 # else:
90 # if not value in self.list:
91 # if self.hideList:
92 # raise Invalid(self.message('invalid', state), value, state)
93 # else:
94 # try:
95 # items = '; '.join(map(str, self.list))
96 # except UnicodeError:
97 # items = '; '.join(map(unicode, self.list))
98 # raise Invalid(
99 # self.message('notIn', state,
100 # items=items, value=value), value, state)
101 #
102 #
103 #
104 #
105 #class Email(FancyValidator):
106 # r"""
107 # Validate an email address.
108 #
109 # If you pass ``resolve_domain=True``, then it will try to resolve
110 # the domain name to make sure it's valid. This takes longer, of
111 # course. You must have the `pyDNS <http://pydns.sf.net>`__ modules
112 # installed to look up DNS (MX and A) records.
113 #
114 # ::
115 #
116 # >>> e = Email()
117 # >>> e.to_python(' test@foo.com ')
118 # 'test@foo.com'
119 # >>> e.to_python('test')
120 # Traceback (most recent call last):
121 # ...
122 # Invalid: An email address must contain a single @
123 # >>> e.to_python('test@foobar')
124 # Traceback (most recent call last):
125 # ...
126 # Invalid: The domain portion of the email address is invalid (the portion after the @: foobar)
127 # >>> e.to_python('test@foobar.com.5')
128 # Traceback (most recent call last):
129 # ...
130 # Invalid: The domain portion of the email address is invalid (the portion after the @: foobar.com.5)
131 # >>> e.to_python('test@foo..bar.com')
132 # Traceback (most recent call last):
133 # ...
134 # Invalid: The domain portion of the email address is invalid (the portion after the @: foo..bar.com)
135 # >>> e.to_python('test@.foo.bar.com')
136 # Traceback (most recent call last):
137 # ...
138 # Invalid: The domain portion of the email address is invalid (the portion after the @: .foo.bar.com)
139 # >>> e.to_python('nobody@xn--m7r7ml7t24h.com')
140 # 'nobody@xn--m7r7ml7t24h.com'
141 # >>> e.to_python('o*reilly@test.com')
142 # 'o*reilly@test.com'
143 # >>> e = Email(resolve_domain=True)
144 # >>> e.resolve_domain
145 # True
146 # >>> e.to_python('doesnotexist@colorstudy.com')
147 # 'doesnotexist@colorstudy.com'
148 # >>> e.to_python('test@nyu.edu')
149 # 'test@nyu.edu'
150 # >>> # NOTE: If you do not have PyDNS installed this example won't work:
151 # >>> e.to_python('test@thisdomaindoesnotexistithinkforsure.com')
152 # Traceback (most recent call last):
153 # ...
154 # Invalid: The domain of the email address does not exist (the portion after the @: thisdomaindoesnotexistithinkforsure.com)
155 # >>> e.to_python(u'test@google.com')
156 # u'test@google.com'
157 # >>> e = Email(not_empty=False)
158 # >>> e.to_python('')
159 #
160 # """
161 #
162 # resolve_domain = False
163 # resolve_timeout = 10 # timeout in seconds when resolving domains
164 #
165 # usernameRE = re.compile(r"^[^ \t\n\r@<>()]+$", re.I)
166 # domainRE = re.compile(r'''
167 # ^(?:[a-z0-9][a-z0-9\-]{0,62}\.)+ # (sub)domain - alpha followed by 62max chars (63 total)
168 # [a-z]{2,}$ # TLD
169 # ''', re.I | re.VERBOSE)
170 #
171 # messages = dict(
172 # empty=_('Please enter an email address'),
173 # noAt=_('An email address must contain a single @'),
174 # badUsername=_('The username portion of the email address is invalid'
175 # ' (the portion before the @: %(username)s)'),
176 # socketError=_('An error occured when trying to connect to the server:'
177 # ' %(error)s'),
178 # badDomain=_('The domain portion of the email address is invalid'
179 # ' (the portion after the @: %(domain)s)'),
180 # domainDoesNotExist=_('The domain of the email address does not exist'
181 # ' (the portion after the @: %(domain)s)'))
182 #
183 # def __init__(self, *args, **kw):
184 # FancyValidator.__init__(self, *args, **kw)
185 # if self.resolve_domain:
186 # if not have_dns:
187 # warnings.warn(
188 # "pyDNS <http://pydns.sf.net> is not installed on"
189 # " your system (or the DNS package cannot be found)."
190 # " I cannot resolve domain names in addresses")
191 # raise ImportError("no module named DNS")
192 #
193 # def validate_python(self, value, state):
194 # if not value:
195 # raise Invalid(self.message('empty', state), value, state)
196 # value = value.strip()
197 # splitted = value.split('@', 1)
198 # try:
199 # username, domain=splitted
200 # except ValueError:
201 # raise Invalid(self.message('noAt', state), value, state)
202 # if not self.usernameRE.search(username):
203 # raise Invalid(
204 # self.message('badUsername', state, username=username),
205 # value, state)
206 # if not self.domainRE.search(domain):
207 # raise Invalid(
208 # self.message('badDomain', state, domain=domain),
209 # value, state)
210 # if self.resolve_domain:
211 # assert have_dns, "pyDNS should be available"
212 # global socket
213 # if socket is None:
214 # import socket
215 # try:
216 # answers = DNS.DnsRequest(domain, qtype='a',
217 # timeout=self.resolve_timeout).req().answers
218 # if answers:
219 # answers = DNS.DnsRequest(domain, qtype='mx',
220 # timeout=self.resolve_timeout).req().answers
221 # except (socket.error, DNS.DNSError), e:
222 # raise Invalid(
223 # self.message('socketError', state, error=e),
224 # value, state)
225 # if not answers:
226 # raise Invalid(
227 # self.message('domainDoesNotExist', state, domain=domain),
228 # value, state)
229 #
230 # def _to_python(self, value, state):
231 # return value.strip()
232 #
233 #
234 #class URL(FancyValidator):
235 # """
236 # Validate a URL, either http://... or https://. If check_exists
237 # is true, then we'll actually make a request for the page.
238 #
239 # If add_http is true, then if no scheme is present we'll add
240 # http://
241 #
242 # ::
243 #
244 # >>> u = URL(add_http=True)
245 # >>> u.to_python('foo.com')
246 # 'http://foo.com'
247 # >>> u.to_python('http://hahaha.ha/bar.html')
248 # 'http://hahaha.ha/bar.html'
249 # >>> u.to_python('http://xn--m7r7ml7t24h.com')
250 # 'http://xn--m7r7ml7t24h.com'
251 # >>> u.to_python('http://foo.com/test?bar=baz&fleem=morx')
252 # 'http://foo.com/test?bar=baz&fleem=morx'
253 # >>> u.to_python('http://foo.com/login?came_from=http%3A%2F%2Ffoo.com%2Ftest')
254 # 'http://foo.com/login?came_from=http%3A%2F%2Ffoo.com%2Ftest'
255 # >>> u.to_python('http://foo.com:8000/test.html')
256 # 'http://foo.com:8000/test.html'
257 # >>> u.to_python('http://foo.com/something\\nelse')
258 # Traceback (most recent call last):
259 # ...
260 # Invalid: That is not a valid URL
261 # >>> u.to_python('https://test.com')
262 # 'https://test.com'
263 # >>> u.to_python('http://test')
264 # Traceback (most recent call last):
265 # ...
266 # Invalid: You must provide a full domain name (like test.com)
267 # >>> u.to_python('http://test..com')
268 # Traceback (most recent call last):
269 # ...
270 # Invalid: That is not a valid URL
271 # >>> u = URL(add_http=False, check_exists=True)
272 # >>> u.to_python('http://google.com')
273 # 'http://google.com'
274 # >>> u.to_python('google.com')
275 # Traceback (most recent call last):
276 # ...
277 # Invalid: You must start your URL with http://, https://, etc
278 # >>> u.to_python('http://formencode.org/doesnotexist.html')
279 # Traceback (most recent call last):
280 # ...
281 # Invalid: The server responded that the page could not be found
282 # >>> u.to_python('http://this.domain.does.not.exist.example.org/test.html')
283 # ... # doctest: +ELLIPSIS
284 # Traceback (most recent call last):
285 # ...
286 # Invalid: An error occured when trying to connect to the server: ...
287 #
288 # If you want to allow addresses without a TLD (e.g., ``localhost``) you can do::
289 #
290 # >>> URL(require_tld=False).to_python('http://localhost')
291 # 'http://localhost'
292 #
293 # """
294 #
295 # check_exists = False
296 # add_http = True
297 # require_tld = True
298 #
299 # url_re = re.compile(r'''
300 # ^(http|https)://
301 # (?:[%:\w]*@)? # authenticator
302 # (?P<domain>[a-z0-9][a-z0-9\-]{,62}\.)* # (sub)domain - alpha followed by 62max chars (63 total)
303 # (?P<tld>[a-z]{2,}) # TLD
304 # (?::[0-9]+)? # port
305 #
306 # # files/delims/etc
307 # (?P<path>/[a-z0-9\-\._~:/\?#\[\]@!%\$&\'\(\)\*\+,;=]*)?
308 # $
309 # ''', re.I | re.VERBOSE)
310 #
311 # scheme_re = re.compile(r'^[a-zA-Z]+:')
312 #
313 # messages = dict(
314 # noScheme=_('You must start your URL with http://, https://, etc'),
315 # badURL=_('That is not a valid URL'),
316 # httpError=_('An error occurred when trying to access the URL:'
317 # ' %(error)s'),
318 # socketError=_('An error occured when trying to connect to the server:'
319 # ' %(error)s'),
320 # notFound=_('The server responded that the page could not be found'),
321 # status=_('The server responded with a bad status code (%(status)s)'),
322 # noTLD=_('You must provide a full domain name (like %(domain)s.com)'))
323 #
324 # def _to_python(self, value, state):
325 # value = value.strip()
326 # if self.add_http:
327 # if not self.scheme_re.search(value):
328 # value = 'http://' + value
329 # match = self.scheme_re.search(value)
330 # if not match:
331 # raise Invalid(self.message('noScheme', state), value, state)
332 # value = match.group(0).lower() + value[len(match.group(0)):]
333 # match = self.url_re.search(value)
334 # if not match:
335 # raise Invalid(self.message('badURL', state), value, state)
336 # if self.require_tld and not match.group('domain'):
337 # raise Invalid(
338 # self.message('noTLD', state, domain=match.group('tld')),
339 # value, state)
340 # if self.check_exists and (
341 # value.startswith('http://') or value.startswith('https://')):
342 # self._check_url_exists(value, state)
343 # return value
344 #
345 # def _check_url_exists(self, url, state):
346 # global httplib, urlparse, socket
347 # if httplib is None:
348 # import httplib
349 # if urlparse is None:
350 # import urlparse
351 # if socket is None:
352 # import socket
353 # scheme, netloc, path, params, query, fragment = urlparse.urlparse(
354 # url, 'http')
355 # if scheme == 'http':
356 # ConnClass = httplib.HTTPConnection
357 # else:
358 # ConnClass = httplib.HTTPSConnection
359 # try:
360 # conn = ConnClass(netloc)
361 # if params:
362 # path += ';' + params
363 # if query:
364 # path += '?' + query
365 # conn.request('HEAD', path)
366 # res = conn.getresponse()
367 # except httplib.HTTPException, e:
368 # raise Invalid(
369 # self.message('httpError', state, error=e), state, url)
370 # except socket.error, e:
371 # raise Invalid(
372 # self.message('socketError', state, error=e), state, url)
373 # else:
374 # if res.status == 404:
375 # raise Invalid(
376 # self.message('notFound', state), state, url)
377 # if not 200 <= res.status < 500:
378 # raise Invalid(
379 # self.message('status', state, status=res.status),
380 # state, url)
381 #
382 #
383 #class XRI(FancyValidator):
384 # r"""
385 # Validator for XRIs.
386 #
387 # It supports both i-names and i-numbers, of the first version of the XRI
388 # standard.
389 #
390 # ::
391 #
392 # >>> inames = XRI(xri_type="i-name")
393 # >>> inames.to_python(" =John.Smith ")
394 # '=John.Smith'
395 # >>> inames.to_python("@Free.Software.Foundation")
396 # '@Free.Software.Foundation'
397 # >>> inames.to_python("Python.Software.Foundation")
398 # Traceback (most recent call last):
399 # ...
400 # Invalid: The type of i-name is not defined; it may be either individual or organizational
401 # >>> inames.to_python("http://example.org")
402 # Traceback (most recent call last):
403 # ...
404 # Invalid: The type of i-name is not defined; it may be either individual or organizational
405 # >>> inames.to_python("=!2C43.1A9F.B6F6.E8E6")
406 # Traceback (most recent call last):
407 # ...
408 # Invalid: "!2C43.1A9F.B6F6.E8E6" is an invalid i-name
409 # >>> iname_with_schema = XRI(True, xri_type="i-name")
410 # >>> iname_with_schema.to_python("=Richard.Stallman")
411 # 'xri://=Richard.Stallman'
412 # >>> inames.to_python("=John Smith")
413 # Traceback (most recent call last):
414 # ...
415 # Invalid: "John Smith" is an invalid i-name
416 # >>> inumbers = XRI(xri_type="i-number")
417 # >>> inumbers.to_python("!!1000!de21.4536.2cb2.8074")
418 # '!!1000!de21.4536.2cb2.8074'
419 # >>> inumbers.to_python("@!1000.9554.fabd.129c!2847.df3c")
420 # '@!1000.9554.fabd.129c!2847.df3c'
421 #
422 # """
423 #
424 # iname_valid_pattern = re.compile(r"""
425 # ^
426 # [\w]+ # A global alphanumeric i-name
427 # (\.[\w]+)* # An i-name with dots
428 # (\*[\w]+(\.[\w]+)*)* # A community i-name
429 # $
430 # """, re.VERBOSE|re.UNICODE)
431 #
432 #
433 # iname_invalid_start = re.compile(r"^[\d\.-]", re.UNICODE)
434 # """@cvar: These characters must not be at the beggining of the i-name"""
435 #
436 # inumber_pattern = re.compile(r"""
437 # ^
438 # (
439 # [=@]! # It's a personal or organization i-number
440 # |
441 # !! # It's a network i-number
442 # )
443 # [\dA-F]{1,4}(\.[\dA-F]{1,4}){0,3} # A global i-number
444 # (![\dA-F]{1,4}(\.[\dA-F]{1,4}){0,3})* # Zero or more sub i-numbers
445 # $
446 # """, re.VERBOSE|re.IGNORECASE)
447 #
448 # messages = dict(
449 # noType=_('The type of i-name is not defined;'
450 # ' it may be either individual or organizational'),
451 # repeatedChar=_('Dots and dashes may not be repeated consecutively'),
452 # badIname=_('"%(iname)s" is an invalid i-name'),
453 # badInameStart=_('i-names may not start with numbers'
454 # ' nor punctuation marks'),
455 # badInumber=_('"%(inumber)s" is an invalid i-number'),
456 # badType=_('The XRI must be a string (not a %(type)s: %(value)r)'),
457 # badXri=_('"%(xri_type)s" is not a valid type of XRI'))
458 #
459 # def __init__(self, add_xri=False, xri_type="i-name", **kwargs):
460 # """Create an XRI validator.
461 #
462 # @param add_xri: Should the schema be added if not present?
463 # Officially it's optional.
464 # @type add_xri: C{bool}
465 # @param xri_type: What type of XRI should be validated?
466 # Possible values: C{i-name} or C{i-number}.
467 # @type xri_type: C{str}
468 #
469 # """
470 # self.add_xri = add_xri
471 # assert xri_type in ('i-name', 'i-number'), (
472 # 'xri_type must be "i-name" or "i-number"')
473 # self.xri_type = xri_type
474 # super(XRI, self).__init__(**kwargs)
475 #
476 # def _to_python(self, value, state):
477 # """Prepend the 'xri://' schema if needed and remove trailing spaces"""
478 # value = value.strip()
479 # if self.add_xri and not value.startswith('xri://'):
480 # value = 'xri://' + value
481 # return value
482 #
483 # def validate_python(self, value, state=None):
484 # """Validate an XRI
485 #
486 # @raise Invalid: If at least one of the following conditions in met:
487 # - C{value} is not a string.
488 # - The XRI is not a personal, organizational or network one.
489 # - The relevant validator (i-name or i-number) considers the XRI
490 # is not valid.
491 #
492 # """
493 # if not isinstance(value, basestring):
494 # raise Invalid(
495 # self.message('badType', state,
496 # type=str(type(value)), value=value), value, state)
497 #
498 # # Let's remove the schema, if any
499 # if value.startswith('xri://'):
500 # value = value[6:]
501 #
502 # if not value[0] in ('@', '=') and not (
503 # self.xri_type == 'i-number' and value[0] == '!'):
504 # raise Invalid(self.message('noType', state), value, state)
505 #
506 # if self.xri_type == 'i-name':
507 # self._validate_iname(value, state)
508 # else:
509 # self._validate_inumber(value, state)
510 #
511 # def _validate_iname(self, iname, state):
512 # """Validate an i-name"""
513 # # The type is not required here:
514 # iname = iname[1:]
515 # if '..' in iname or '--' in iname:
516 # raise Invalid(self.message('repeatedChar', state), iname, state)
517 # if self.iname_invalid_start.match(iname):
518 # raise Invalid(self.message('badInameStart', state), iname, state)
519 # if not self.iname_valid_pattern.match(iname) or '_' in iname:
520 # raise Invalid(
521 # self.message('badIname', state, iname=iname), iname, state)
522 #
523 # def _validate_inumber(self, inumber, state):
524 # """Validate an i-number"""
525 # if not self.__class__.inumber_pattern.match(inumber):
526 # raise Invalid(
527 # self.message('badInumber', state,
528 # inumber=inumber, value=inumber), inumber, state)
529 #
530 #
531 #class OpenId(FancyValidator):
532 # r"""
533 # OpenId validator.
534 #
535 # ::
536 # >>> v = OpenId(add_schema=True)
537 # >>> v.to_python(' example.net ')
538 # 'http://example.net'
539 # >>> v.to_python('@TurboGears')
540 # 'xri://@TurboGears'
541 # >>> w = OpenId(add_schema=False)
542 # >>> w.to_python(' example.net ')
543 # Traceback (most recent call last):
544 # ...
545 # Invalid: "example.net" is not a valid OpenId (it is neither an URL nor an XRI)
546 # >>> w.to_python('!!1000')
547 # '!!1000'
548 # >>> w.to_python('look@me.com')
549 # Traceback (most recent call last):
550 # ...
551 # Invalid: "look@me.com" is not a valid OpenId (it is neither an URL nor an XRI)
552 #
553 # """
554 #
555 # messages = dict(
556 # badId=_('"%(id)s" is not a valid OpenId'
557 # ' (it is neither an URL nor an XRI)'))
558 #
559 # def __init__(self, add_schema=False, **kwargs):
560 # """Create an OpenId validator.
561 #
562 # @param add_schema: Should the schema be added if not present?
563 # @type add_schema: C{bool}
564 #
565 # """
566 # self.url_validator = URL(add_http=add_schema)
567 # self.iname_validator = XRI(add_schema, xri_type="i-name")
568 # self.inumber_validator = XRI(add_schema, xri_type="i-number")
569 #
570 # def _to_python(self, value, state):
571 # value = value.strip()
572 # try:
573 # return self.url_validator.to_python(value, state)
574 # except Invalid:
575 # try:
576 # return self.iname_validator.to_python(value, state)
577 # except Invalid:
578 # try:
579 # return self.inumber_validator.to_python(value, state)
580 # except Invalid:
581 # pass
582 # # It's not an OpenId!
583 # raise Invalid(self.message('badId', state, id=value), value, state)
584 #
585 # def validate_python(self, value, state):
586 # self._to_python(value, state)
587 #
588 #
589 #def StateProvince(*kw, **kwargs):
590 # warnings.warn("please use formencode.national.USStateProvince",
591 # DeprecationWarning, stacklevel=2)
592 # from formencode.national import USStateProvince
593 # return USStateProvince(*kw, **kwargs)
594 #
595 #
596 #def PhoneNumber(*kw, **kwargs):
597 # warnings.warn("please use formencode.national.USPhoneNumber",
598 # DeprecationWarning, stacklevel=2)
599 # from formencode.national import USPhoneNumber
600 # return USPhoneNumber(*kw, **kwargs)
601 #
602 #
603 #def IPhoneNumberValidator(*kw, **kwargs):
604 # warnings.warn("please use formencode.national.InternationalPhoneNumber",
605 # DeprecationWarning, stacklevel=2)
606 # from formencode.national import InternationalPhoneNumber
607 # return InternationalPhoneNumber(*kw, **kwargs)
608 #
609 #
610 #class FieldStorageUploadConverter(FancyValidator):
611 # """
612 # Handles cgi.FieldStorage instances that are file uploads.
613 #
614 # This doesn't do any conversion, but it can detect empty upload
615 # fields (which appear like normal fields, but have no filename when
616 # no upload was given).
617 # """
618 # def _to_python(self, value, state=None):
619 # if isinstance(value, cgi.FieldStorage):
620 # if getattr(value, 'filename', None):
621 # return value
622 # raise Invalid('invalid', value, state)
623 # else:
624 # return value
625 #
626 # def is_empty(self, value):
627 # if isinstance(value, cgi.FieldStorage):
628 # return not bool(getattr(value, 'filename', None))
629 # return FancyValidator.is_empty(self, value)
630 #
631 #
632 #class FileUploadKeeper(FancyValidator):
633 # """
634 # Takes two inputs (a dictionary with keys ``static`` and
635 # ``upload``) and converts them into one value on the Python side (a
636 # dictionary with ``filename`` and ``content`` keys). The upload
637 # takes priority over the static value. The filename may be None if
638 # it can't be discovered.
639 #
640 # Handles uploads of both text and ``cgi.FieldStorage`` upload
641 # values.
642 #
643 # This is basically for use when you have an upload field, and you
644 # want to keep the upload around even if the rest of the form
645 # submission fails. When converting *back* to the form submission,
646 # there may be extra values ``'original_filename'`` and
647 # ``'original_content'``, which may want to use in your form to show
648 # the user you still have their content around.
649 #
650 # To use this, make sure you are using variabledecode, then use
651 # something like::
652 #
653 # <input type="file" name="myfield.upload">
654 # <input type="hidden" name="myfield.static">
655 #
656 # Then in your scheme::
657 #
658 # class MyScheme(Scheme):
659 # myfield = FileUploadKeeper()
660 #
661 # Note that big file uploads mean big hidden fields, and lots of
662 # bytes passed back and forth in the case of an error.
663 # """
664 #
665 # upload_key = 'upload'
666 # static_key = 'static'
667 #
668 # def _to_python(self, value, state):
669 # upload = value.get(self.upload_key)
670 # static = value.get(self.static_key, '').strip()
671 # filename = content = None
672 # if isinstance(upload, cgi.FieldStorage):
673 # filename = upload.filename
674 # content = upload.value
675 # elif isinstance(upload, basestring) and upload:
676 # filename = None
677 # # @@: Should this encode upload if it is unicode?
678 # content = upload
679 # if not content and static:
680 # filename, content = static.split(None, 1)
681 # if filename == '-':
682 # filename = ''
683 # else:
684 # filename = filename.decode('base64')
685 # content = content.decode('base64')
686 # return {'filename': filename, 'content': content}
687 #
688 # def _from_python(self, value, state):
689 # filename = value.get('filename', '')
690 # content = value.get('content', '')
691 # if filename or content:
692 # result = self.pack_content(filename, content)
693 # return {self.upload_key: '',
694 # self.static_key: result,
695 # 'original_filename': filename,
696 # 'original_content': content}
697 # else:
698 # return {self.upload_key: '',
699 # self.static_key: ''}
700 #
701 # def pack_content(self, filename, content):
702 # enc_filename = self.base64encode(filename) or '-'
703 # enc_content = (content or '').encode('base64')
704 # result = '%s %s' % (enc_filename, enc_content)
705 # return result
706 #
707 #
708 #class DateConverter(FancyValidator):
709 # """
710 # Validates and converts a string date, like mm/yy, dd/mm/yy,
711 # dd-mm-yy, etc. Using ``month_style`` you can support
712 # ``'mm/dd/yyyy'`` or ``'dd/mm/yyyy'``. Only these two general
713 # styles are supported.
714 #
715 # Accepts English month names, also abbreviated. Returns value as a
716 # datetime object (you can get mx.DateTime objects if you use
717 # ``datetime_module='mxDateTime'``). Two year dates are assumed to
718 # be within 1950-2020, with dates from 21-49 being ambiguous and
719 # signaling an error.
720 #
721 # Use accept_day=False if you just want a month/year (like for a
722 # credit card expiration date).
723 #
724 # ::
725 #
726 # >>> d = DateConverter()
727 # >>> d.to_python('12/3/09')
728 # datetime.date(2009, 12, 3)
729 # >>> d.to_python('12/3/2009')
730 # datetime.date(2009, 12, 3)
731 # >>> d.to_python('2/30/04')
732 # Traceback (most recent call last):
733 # ...
734 # Invalid: That month only has 29 days
735 # >>> d.to_python('13/2/05')
736 # Traceback (most recent call last):
737 # ...
738 # Invalid: Please enter a month from 1 to 12
739 # >>> d.to_python('1/1/200')
740 # Traceback (most recent call last):
741 # ...
742 # Invalid: Please enter a four-digit year after 1899
743 #
744 # If you change ``month_style`` you can get European-style dates::
745 #
746 # >>> d = DateConverter(month_style='dd/mm/yyyy')
747 # >>> date = d.to_python('12/3/09')
748 # >>> date
749 # datetime.date(2009, 3, 12)
750 # >>> d.from_python(date)
751 # '12/03/2009'
752 # """
753 # ## @@: accepts only US-style dates
754 #
755 # accept_day = True
756 # # also allowed: 'dd/mm/yyyy'
757 # month_style = 'mm/dd/yyyy'
758 # # Use 'datetime' to force the Python 2.3+ datetime module, or
759 # # 'mxDateTime' to force the mxDateTime module (None means use
760 # # datetime, or if not present mxDateTime)
761 # datetime_module = None
762 #
763 # _day_date_re = re.compile(r'^\s*(\d\d?)[\-\./\\](\d\d?|jan|january|feb|febuary|mar|march|apr|april|may|jun|june|jul|july|aug|august|sep|sept|september|oct|october|nov|november|dec|december)[\-\./\\](\d\d\d?\d?)\s*$', re.I)
764 # _month_date_re = re.compile(r'^\s*(\d\d?|jan|january|feb|febuary|mar|march|apr|april|may|jun|june|jul|july|aug|august|sep|sept|september|oct|october|nov|november|dec|december)[\-\./\\](\d\d\d?\d?)\s*$', re.I)
765 #
766 # _month_names = {
767 # 'jan': 1, 'january': 1,
768 # 'feb': 2, 'febuary': 2,
769 # 'mar': 3, 'march': 3,
770 # 'apr': 4, 'april': 4,
771 # 'may': 5,
772 # 'jun': 6, 'june': 6,
773 # 'jul': 7, 'july': 7,
774 # 'aug': 8, 'august': 8,
775 # 'sep': 9, 'sept': 9, 'september': 9,
776 # 'oct': 10, 'october': 10,
777 # 'nov': 11, 'november': 11,
778 # 'dec': 12, 'december': 12,
779 # }
780 #
781 # ## @@: Feb. should be leap-year aware (but mxDateTime does catch that)
782 # _monthDays = {
783 # 1: 31, 2: 29, 3: 31, 4: 30, 5: 31, 6: 30, 7: 31, 8: 31,
784 # 9: 30, 10: 31, 11: 30, 12: 31}
785 #
786 # messages = dict(
787 # badFormat=_('Please enter the date in the form %(format)s'),
788 # monthRange=_('Please enter a month from 1 to 12'),
789 # invalidDay=_('Please enter a valid day'),
790 # dayRange=_('That month only has %(days)i days'),
791 # invalidDate=_('That is not a valid day (%(exception)s)'),
792 # unknownMonthName=_('Unknown month name: %(month)s'),
793 # invalidYear=_('Please enter a number for the year'),
794 # fourDigitYear=_('Please enter a four-digit year after 1899'),
795 # wrongFormat=_('Please enter the date in the form %(format)s'))
796 #
797 # def __init__(self, *args, **kw):
798 # super(DateConverter, self).__init__(*args, **kw)
799 # if not self.month_style in ('dd/mm/yyyy', 'mm/dd/yyyy'):
800 # raise TypeError('Bad month_style: %r' % self.month_style)
801 #
802 # def _to_python(self, value, state):
803 # if self.accept_day:
804 # return self.convert_day(value, state)
805 # else:
806 # return self.convert_month(value, state)
807 #
808 # def convert_day(self, value, state):
809 # self.assert_string(value, state)
810 # match = self._day_date_re.search(value)
811 # if not match:
812 # raise Invalid(
813 # self.message('badFormat', state,
814 # format=self.month_style), value, state)
815 # day = int(match.group(1))
816 # try:
817 # month = int(match.group(2))
818 # except (TypeError, ValueError):
819 # month = self.make_month(match.group(2), state)
820 # else:
821 # if self.month_style == 'mm/dd/yyyy':
822 # month, day = day, month
823 # year = self.make_year(match.group(3), state)
824 # if not 1 <= month <= 12:
825 # raise Invalid(self.message('monthRange', state), value, state)
826 # if day < 1:
827 # raise Invalid(self.message('invalidDay', state), value, state)
828 # if self._monthDays[month] < day:
829 # raise Invalid(
830 # self.message('dayRange', state,
831 # days=self._monthDays[month]), value, state)
832 # dt_mod = import_datetime(self.datetime_module)
833 # try:
834 # return datetime_makedate(dt_mod, year, month, day)
835 # except ValueError, v:
836 # raise Invalid(
837 # self.message('invalidDate', state,
838 # exception=str(v)), value, state)
839 #
840 # def make_month(self, value, state):
841 # try:
842 # return int(value)
843 # except ValueError:
844 # value = value.lower().strip()
845 # if value in self._month_names:
846 # return self._month_names[value]
847 # else:
848 # raise Invalid(
849 # self.message('unknownMonthName', state,
850 # month=value), value, state)
851 #
852 # def make_year(self, year, state):
853 # try:
854 # year = int(year)
855 # except ValueError:
856 # raise Invalid(self.message('invalidYear', state), year, state)
857 # if year <= 20:
858 # year += 2000
859 # elif 50 <= year < 100:
860 # year += 1900
861 # if 20 < year < 50 or 99 < year < 1900:
862 # raise Invalid(self.message('fourDigitYear', state), year, state)
863 # return year
864 #
865 # def convert_month(self, value, state):
866 # match = self._month_date_re.search(value)
867 # if not match:
868 # raise Invalid(
869 # self.message('wrongFormat', state,
870 # format='mm/yyyy'), value, state)
871 # month = self.make_month(match.group(1), state)
872 # year = self.make_year(match.group(2), state)
873 # if not 1 <= month <= 12:
874 # raise Invalid(self.message('monthRange', state), value, state)
875 # dt_mod = import_datetime(self.datetime_module)
876 # return datetime_makedate(dt_mod, year, month, 1)
877 #
878 # def _from_python(self, value, state):
879 # if self.if_empty is not NoDefault and not value:
880 # return ''
881 # if self.accept_day:
882 # return self.unconvert_day(value, state)
883 # else:
884 # return self.unconvert_month(value, state)
885 #
886 # def unconvert_day(self, value, state):
887 # # @@ ib: double-check, improve
888 # if self.month_style == 'mm/dd/yyyy':
889 # return value.strftime('%m/%d/%Y')
890 # else:
891 # return value.strftime('%d/%m/%Y')
892 #
893 # def unconvert_month(self, value, state):
894 # # @@ ib: double-check, improve
895 # return value.strftime('%m/%Y')
896 #
897 #
898 #class TimeConverter(FancyValidator):
899 # """
900 # Converts times in the format HH:MM:SSampm to (h, m, s).
901 # Seconds are optional.
902 #
903 # For ampm, set use_ampm = True. For seconds, use_seconds = True.
904 # Use 'optional' for either of these to make them optional.
905 #
906 # Examples::
907 #
908 # >>> tim = TimeConverter()
909 # >>> tim.to_python('8:30')
910 # (8, 30)
911 # >>> tim.to_python('20:30')
912 # (20, 30)
913 # >>> tim.to_python('30:00')
914 # Traceback (most recent call last):
915 # ...
916 # Invalid: You must enter an hour in the range 0-23
917 # >>> tim.to_python('13:00pm')
918 # Traceback (most recent call last):
919 # ...
920 # Invalid: You must enter an hour in the range 1-12
921 # >>> tim.to_python('12:-1')
922 # Traceback (most recent call last):
923 # ...
924 # Invalid: You must enter a minute in the range 0-59
925 # >>> tim.to_python('12:02pm')
926 # (12, 2)
927 # >>> tim.to_python('12:02am')
928 # (0, 2)
929 # >>> tim.to_python('1:00PM')
930 # (13, 0)
931 # >>> tim.from_python((13, 0))
932 # '13:00:00'
933 # >>> tim2 = tim(use_ampm=True, use_seconds=False)
934 # >>> tim2.from_python((13, 0))
935 # '1:00pm'
936 # >>> tim2.from_python((0, 0))
937 # '12:00am'
938 # >>> tim2.from_python((12, 0))
939 # '12:00pm'
940 #
941 # Examples with ``datetime.time``::
942 #
943 # >>> v = TimeConverter(use_datetime=True)
944 # >>> a = v.to_python('18:00')
945 # >>> a
946 # datetime.time(18, 0)
947 # >>> b = v.to_python('30:00')
948 # Traceback (most recent call last):
949 # ...
950 # Invalid: You must enter an hour in the range 0-23
951 # >>> v2 = TimeConverter(prefer_ampm=True, use_datetime=True)
952 # >>> v2.from_python(a)
953 # '6:00:00pm'
954 # >>> v3 = TimeConverter(prefer_ampm=True,
955 # ... use_seconds=False, use_datetime=True)
956 # >>> a = v3.to_python('18:00')
957 # >>> a
958 # datetime.time(18, 0)
959 # >>> v3.from_python(a)
960 # '6:00pm'
961 # >>> a = v3.to_python('18:00:00')
962 # Traceback (most recent call last):
963 # ...
964 # Invalid: You may not enter seconds
965 # """
966 #
967 # use_ampm = 'optional'
968 # prefer_ampm = False
969 # use_seconds = 'optional'
970 # use_datetime = False
971 # # This can be set to make it prefer mxDateTime:
972 # datetime_module = None
973 #
974 # messages = dict(
975 # noAMPM=_('You must indicate AM or PM'),
976 # tooManyColon=_('There are too many :\'s'),
977 # noSeconds=_('You may not enter seconds'),
978 # secondsRequired=_('You must enter seconds'),
979 # minutesRequired=_('You must enter minutes (after a :)'),
980 # badNumber=_('The %(part)s value you gave is not a number: %(number)r'),
981 # badHour=_('You must enter an hour in the range %(range)s'),
982 # badMinute=_('You must enter a minute in the range 0-59'),
983 # badSecond=_('You must enter a second in the range 0-59'))
984 #
985 # def _to_python(self, value, state):
986 # result = self._to_python_tuple(value, state)
987 # if self.use_datetime:
988 # dt_mod = import_datetime(self.datetime_module)
989 # time_class = datetime_time(dt_mod)
990 # return time_class(*result)
991 # else:
992 # return result
993 #
994 # def _to_python_tuple(self, value, state):
995 # time = value.strip()
996 # explicit_ampm = False
997 # if self.use_ampm:
998 # last_two = time[-2:].lower()
999 # if last_two not in ('am', 'pm'):
1000 # if self.use_ampm != 'optional':
1001 # raise Invalid(self.message('noAMPM', state), value, state)
1002 # else:
1003 # offset = 0
1004 # else:
1005 # explicit_ampm = True
1006 # if last_two == 'pm':
1007 # offset = 12
1008 # else:
1009 # offset = 0
1010 # time = time[:-2]
1011 # else:
1012 # offset = 0
1013 # parts = time.split(':')
1014 # if len(parts) > 3:
1015 # raise Invalid(self.message('tooManyColon', state), value, state)
1016 # if len(parts) == 3 and not self.use_seconds:
1017 # raise Invalid(self.message('noSeconds', state), value, state)
1018 # if (len(parts) == 2
1019 # and self.use_seconds and self.use_seconds != 'optional'):
1020 # raise Invalid(self.message('secondsRequired', state), value, state)
1021 # if len(parts) == 1:
1022 # raise Invalid(self.message('minutesRequired', state), value, state)
1023 # try:
1024 # hour = int(parts[0])
1025 # except ValueError:
1026 # raise Invalid(
1027 # self.message('badNumber', state,
1028 # number=parts[0], part='hour'), value, state)
1029 # if explicit_ampm:
1030 # if not 1 <= hour <= 12:
1031 # raise Invalid(
1032 # self.message('badHour', state,
1033 # number=hour, range='1-12'), value, state)
1034 # if hour == 12 and offset == 12:
1035 # # 12pm == 12
1036 # pass
1037 # elif hour == 12 and offset == 0:
1038 # # 12am == 0
1039 # hour = 0
1040 # else:
1041 # hour += offset
1042 # else:
1043 # if not 0 <= hour < 24:
1044 # raise Invalid(
1045 # self.message('badHour', state,
1046 # number=hour, range='0-23'), value, state)
1047 # try:
1048 # minute = int(parts[1])
1049 # except ValueError:
1050 # raise Invalid(
1051 # self.message('badNumber', state,
1052 # number=parts[1], part='minute'), value, state)
1053 # if not 0 <= minute < 60:
1054 # raise Invalid(
1055 # self.message('badMinute', state, number=minute),
1056 # value, state)
1057 # if len(parts) == 3:
1058 # try:
1059 # second = int(parts[2])
1060 # except ValueError:
1061 # raise Invalid(
1062 # self.message('badNumber', state,
1063 # number=parts[2], part='second'), value, state)
1064 # if not 0 <= second < 60:
1065 # raise Invalid(
1066 # self.message('badSecond', state, number=second),
1067 # value, state)
1068 # else:
1069 # second = None
1070 # if second is None:
1071 # return (hour, minute)
1072 # else:
1073 # return (hour, minute, second)
1074 #
1075 # def _from_python(self, value, state):
1076 # if isinstance(value, basestring):
1077 # return value
1078 # if hasattr(value, 'hour'):
1079 # hour, minute = value.hour, value.minute
1080 # second = value.second
1081 # elif len(value) == 3:
1082 # hour, minute, second = value
1083 # elif len(value) == 2:
1084 # hour, minute = value
1085 # second = 0
1086 # ampm = ''
1087 # if (self.use_ampm == 'optional' and self.prefer_ampm) or (
1088 # self.use_ampm and self.use_ampm != 'optional'):
1089 # ampm = 'am'
1090 # if hour > 12:
1091 # hour -= 12
1092 # ampm = 'pm'
1093 # elif hour == 12:
1094 # ampm = 'pm'
1095 # elif hour == 0:
1096 # hour = 12
1097 # if self.use_seconds:
1098 # return '%i:%02i:%02i%s' % (hour, minute, second, ampm)
1099 # else:
1100 # return '%i:%02i%s' % (hour, minute, ampm)
1101 #
1102 #
1103 #def PostalCode(*kw, **kwargs):
1104 # warnings.warn("please use formencode.national.USPostalCode",
1105 # DeprecationWarning, stacklevel=2)
1106 # from formencode.national import USPostalCode
1107 # return USPostalCode(*kw, **kwargs)
1108 #
1109 #
1110 #class StripField(FancyValidator):
1111 # """
1112 # Take a field from a dictionary, removing the key from the dictionary.
1113 #
1114 # ``name`` is the key. The field value and a new copy of the dictionary
1115 # with that field removed are returned.
1116 #
1117 # >>> StripField('test').to_python({'a': 1, 'test': 2})
1118 # (2, {'a': 1})
1119 # >>> StripField('test').to_python({})
1120 # Traceback (most recent call last):
1121 # ...
1122 # Invalid: The name 'test' is missing
1123 #
1124 # """
1125 #
1126 # __unpackargs__ = ('name',)
1127 #
1128 # messages = dict(
1129 # missing=_('The name %(name)s is missing'))
1130 #
1131 # def _to_python(self, valueDict, state):
1132 # v = valueDict.copy()
1133 # try:
1134 # field = v.pop(self.name)
1135 # except KeyError:
1136 # raise Invalid(
1137 # self.message('missing', state, name=repr(self.name)),
1138 # valueDict, state)
1139 # return field, v
1140 #
1141 # def is_empty(self, value):
1142 # # empty dictionaries don't really apply here
1143 # return False
1144 #
1145 #
1146 #
1147 #
1148 #class SignedString(FancyValidator):
1149 # """
1150 # Encodes a string into a signed string, and base64 encodes both the
1151 # signature string and a random nonce.
1152 #
1153 # It is up to you to provide a secret, and to keep the secret handy
1154 # and consistent.
1155 # """
1156 #
1157 # messages = dict(
1158 # malformed=_('Value does not contain a signature'),
1159 # badsig=_('Signature is not correct'))
1160 #
1161 # secret = None
1162 # nonce_length = 4
1163 #
1164 # def _to_python(self, value, state):
1165 # global sha1
1166 # if not sha1:
1167 # try:
1168 # from hashlib import sha1
1169 # except ImportError: # Python < 2.5
1170 # from sha import sha as sha1
1171 # assert self.secret is not None, (
1172 # "You must give a secret")
1173 # parts = value.split(None, 1)
1174 # if not parts or len(parts) == 1:
1175 # raise Invalid(self.message('malformed', state), value, state)
1176 # sig, rest = parts
1177 # sig = sig.decode('base64')
1178 # rest = rest.decode('base64')
1179 # nonce = rest[:self.nonce_length]
1180 # rest = rest[self.nonce_length:]
1181 # expected = sha1(str(self.secret)+nonce+rest).digest()
1182 # if expected != sig:
1183 # raise Invalid(self.message('badsig', state), value, state)
1184 # return rest
1185 #
1186 # def _from_python(self, value, state):
1187 # global sha1
1188 # if not sha1:
1189 # try:
1190 # from hashlib import sha1
1191 # except ImportError:
1192 # from sha import sha as sha1
1193 # nonce = self.make_nonce()
1194 # value = str(value)
1195 # digest = sha1(self.secret+nonce+value).digest()
1196 # return self.encode(digest)+' '+self.encode(nonce+value)
1197 #
1198 # def encode(self, value):
1199 # return value.encode('base64').strip().replace('\n', '')
1200 #
1201 # def make_nonce(self):
1202 # global random
1203 # if not random:
1204 # import random
1205 # return ''.join([
1206 # chr(random.randrange(256))
1207 # for i in range(self.nonce_length)])
1208 #
1209 #
1210 #class IPAddress(FancyValidator):
1211 # """
1212 # Formencode validator to check whether a string is a correct IP address.
1213 #
1214 # Examples::
1215 #
1216 # >>> ip = IPAddress()
1217 # >>> ip.to_python('127.0.0.1')
1218 # '127.0.0.1'
1219 # >>> ip.to_python('299.0.0.1')
1220 # Traceback (most recent call last):
1221 # ...
1222 # Invalid: The octets must be within the range of 0-255 (not '299')
1223 # >>> ip.to_python('192.168.0.1/1')
1224 # Traceback (most recent call last):
1225 # ...
1226 # Invalid: Please enter a valid IP address (a.b.c.d)
1227 # >>> ip.to_python('asdf')
1228 # Traceback (most recent call last):
1229 # ...
1230 # Invalid: Please enter a valid IP address (a.b.c.d)
1231 # """
1232 #
1233 # messages = dict(
1234 # badFormat=_('Please enter a valid IP address (a.b.c.d)'),
1235 # illegalOctets=_('The octets must be within the range of 0-255'
1236 # ' (not %(octet)r)'))
1237 #
1238 # def validate_python(self, value, state):
1239 # try:
1240 # octets = value.split('.')
1241 # # Only 4 octets?
1242 # if len(octets) != 4:
1243 # raise Invalid(
1244 # self.message('badFormat', state, value=value),
1245 # value, state)
1246 # # Correct octets?
1247 # for octet in octets:
1248 # if not 0 <= int(octet) < 256:
1249 # raise Invalid(
1250 # self.message('illegalOctets', state, octet=octet),
1251 # value, state)
1252 # # Splitting faild: wrong syntax
1253 # except ValueError:
1254 # raise Invalid(self.message('badFormat', state), value, state)
1255 #
1256 #
1257 #
1258 ### DEBUG & TEST ###
1259 #
1260 #if __name__ == "__main__":
1261 # import doctest
1262 # doctest.testmod()
1263 #
1264
| Home | Trees | Indices | Help |
|---|
| Generated by Epydoc 3.0.1 on Fri Jul 22 15:13:46 2011 | http://epydoc.sourceforge.net |