Package konval :: Module internetval
[hide private]
[frames] | no frames]

Source Code for Module konval.internetval

   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