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 |