0001## FormEncode, a Form processor
0002## Copyright (C) 2003, Ian Bicking <ianb@colorstudy.com>
0003##
0004## This library is free software; you can redistribute it and/or
0005## modify it under the terms of the GNU Lesser General Public
0006## License as published by the Free Software Foundation; either
0007## version 2.1 of the License, or (at your option) any later version.
0008##
0009## This library is distributed in the hope that it will be useful,
0010## but WITHOUT ANY WARRANTY; without even the implied warranty of
0011## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0012## Lesser General Public License for more details.
0013##
0014## You should have received a copy of the GNU Lesser General Public
0015## License along with this library; if not, write to the Free Software
0016## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0017##
0018## NOTE: In the context of the Python environment, I interpret "dynamic
0019## linking" as importing -- thus the LGPL applies to the contents of
0020## the modules, but make no requirements on code importing these
0021## modules.
0022"""
0023Validator/Converters for use with FormEncode.
0024"""
0025
0026import re
0027DateTime = None
0028httplib = None
0029urlparse = None
0030import socket
0031from interfaces import *
0032from api import *
0033sha = random = None
0034try:
0035 import sets
0036except ImportError:
0037 sets = None
0038
0039import cgi
0040
0041import fieldstorage
0042
0043try:
0044 import DNS
0045 DNS.DiscoverNameServers()
0046 have_dns=True
0047except ImportError:
0048 have_dns=False
0049
0050True, False = (1==1), (0==1)
0051
0052def _(s): return s # dummy translation function, nothing is translated here.
0053 # Instead this is actually done in api.message.
0054 # The surrounding _("string") of the strings is only for extracting
0055 # the strings automatically
0056 # if you run pygettext with this source comment this function out temporarly
0057
0058############################################################
0059## Utility methods
0060############################################################
0061
0062# These all deal with accepting both mxDateTime and datetime
0063# modules and types
0064datetime_module = None
0065mxDateTime_module = None
0066
0067def import_datetime(module_type):
0068 global datetime_module, mxDateTime_module
0069 if module_type is None:
0070 try:
0071 if datetime_module is None:
0072 import datetime as datetime_module
0073 return datetime_module
0074 except ImportError:
0075 if mxDateTime_module is None:
0076 from mx import DateTime as mxDateTime_module
0077 return mxDateTime_module
0078
0079 module_type = module_type.lower()
0080 assert module_type in ('datetime', 'mxdatetime')
0081 if module_type == 'datetime':
0082 if datetime_module is None:
0083 import datetime as datetime_module
0084 return datetime_module
0085 else:
0086 if mxDateTime_module is None:
0087 from mx import DateTime as mxDateTime_module
0088 return mxDateTime_module
0089
0090def datetime_now(module):
0091 if module.__name__ == 'datetime':
0092 return module.datetime.now()
0093 else:
0094 return module.now()
0095
0096def datetime_makedate(module, year, month, day):
0097 if module.__name__ == 'datetime':
0098 return module.date(year, month, day)
0099 else:
0100 try:
0101 return module.DateTime(year, month, day)
0102 except module.RangeError, e:
0103 raise ValueError(str(e))
0104
0105
0106# TODO: Needs being extended to support mx.DateTime as well.
0107def datetime_time(module):
0108 if module.__name__ == 'datetime':
0109 return module.time
0110
0111# TODO: Needs being extended to support mx.DateTime as well.
0112def datetime_isotime(module):
0113 if module.__name__ == 'datetime':
0114 return module.time.isoformat
0115
0116
0117
0118############################################################
0119## Wrapper Validators
0120############################################################
0121
0122class ConfirmType(FancyValidator):
0123
0124 """
0125 Confirms that the input/output is of the proper type.
0126
0127 Uses the parameters:
0128
0129 subclass:
0130 The class or a tuple of classes; the item must be an instance
0131 of the class or a subclass.
0132 type:
0133 A type or tuple of types (or classes); the item must be of
0134 the exact class or type. Subclasses are not allowed.
0135
0136 Examples::
0137
0138 >>> cint = ConfirmType(subclass=int)
0139 >>> cint.to_python(True)
0140 True
0141 >>> cint.to_python('1')
0142 Traceback (most recent call last):
0143 ...
0144 Invalid: '1' is not a subclass of <type 'int'>
0145 >>> cintfloat = ConfirmType(subclass=(float, int))
0146 >>> cintfloat.to_python(1.0), cintfloat.from_python(1.0)
0147 (1.0, 1.0)
0148 >>> cintfloat.to_python(1), cintfloat.from_python(1)
0149 (1, 1)
0150 >>> cintfloat.to_python(None)
0151 Traceback (most recent call last):
0152 ...
0153 Invalid: None is not a subclass of one of the types <type 'float'>, <type 'int'>
0154 >>> cint2 = ConfirmType(type=int)
0155 >>> cint2(accept_python=False).from_python(True)
0156 Traceback (most recent call last):
0157 ...
0158 Invalid: True must be of the type <type 'int'>
0159 """
0160
0161 subclass = None
0162 type = None
0163
0164 messages = {
0165 'subclass': _("%(object)r is not a subclass of %(subclass)s"),
0166 'inSubclass': _("%(object)r is not a subclass of one of the types %(subclassList)s"),
0167 'inType': _("%(object)r must be one of the types %(typeList)s"),
0168 'type': _("%(object)r must be of the type %(type)s"),
0169 }
0170
0171 def __init__(self, *args, **kw):
0172 FancyValidator.__init__(self, *args, **kw)
0173 if self.subclass:
0174 if isinstance(self.subclass, list):
0175 self.subclass = tuple(self.subclass)
0176 elif not isinstance(self.subclass, tuple):
0177 self.subclass = (self.subclass,)
0178 self.validate_python = self.confirm_subclass
0179 if self.type:
0180 if isinstance(self.type, list):
0181 self.type = tuple(self.type)
0182 elif not isinstance(self.type, tuple):
0183 self.type = (self.type,)
0184 self.validate_python = self.confirm_type
0185
0186 def confirm_subclass(self, value, state):
0187 if not isinstance(value, self.subclass):
0188 if len(self.subclass) == 1:
0189 msg = self.message('subclass', state, object=value,
0190 subclass=self.subclass[0])
0191 else:
0192 subclass_list = ', '.join(map(str, self.subclass))
0193 msg = self.message('inSubclass', state, object=value,
0194 subclassList=subclass_list)
0195 raise Invalid(msg, value, state)
0196
0197 def confirm_type(self, value, state):
0198 for t in self.type:
0199 if type(value) is t:
0200 break
0201 else:
0202 if len(self.type) == 1:
0203 msg = self.message('type', state, object=value,
0204 type=self.type[0])
0205 else:
0206 msg = self.message('inType', state, object=value,
0207 typeList=', '.join(map(str, self.type)))
0208 raise Invalid(msg, value, state)
0209 return value
0210
0211 def is_empty(self, value):
0212 return False
0213
0214class Wrapper(FancyValidator):
0215
0216 """
0217 Used to convert functions to validator/converters.
0218
0219 You can give a simple function for `to_python`, `from_python`,
0220 `validate_python` or `validate_other`. If that function raises an
0221 exception, the value is considered invalid. Whatever value the
0222 function returns is considered the converted value.
0223
0224 Unlike validators, the `state` argument is not used. Functions
0225 like `int` can be used here, that take a single argument.
0226
0227 Examples::
0228
0229 >>> def downcase(v):
0230 ... return v.lower()
0231 >>> wrap = Wrapper(to_python=downcase)
0232 >>> wrap.to_python('This')
0233 'this'
0234 >>> wrap.from_python('This')
0235 'This'
0236 >>> wrap2 = Wrapper(from_python=downcase)
0237 >>> wrap2.from_python('This')
0238 'this'
0239 >>> wrap2.from_python(1)
0240 Traceback (most recent call last):
0241 ...
0242 Invalid: 'int' object has no attribute 'lower'
0243 >>> wrap3 = Wrapper(validate_python=int)
0244 >>> wrap3.to_python('1')
0245 '1'
0246 >>> wrap3.to_python('a')
0247 Traceback (most recent call last):
0248 ...
0249 Invalid: invalid literal for int(): a
0250 """
0251
0252 func_to_python = None
0253 func_from_python = None
0254 func_validate_python = None
0255 func_validate_other = None
0256
0257 def __init__(self, *args, **kw):
0258 for n in ['to_python', 'from_python', 'validate_python',
0259 'validate_other']:
0260 if kw.has_key(n):
0261 kw['func_%s' % n] = kw[n]
0262 del kw[n]
0263 FancyValidator.__init__(self, *args, **kw)
0264 self._to_python = self.wrap(self.func_to_python)
0265 self._from_python = self.wrap(self.func_from_python)
0266 self.validate_python = self.wrap(self.func_validate_python)
0267 self.validate_other = self.wrap(self.func_validate_other)
0268
0269 def wrap(self, func):
0270 if not func:
0271 return None
0272 def result(value, state, func=func):
0273 try:
0274 return func(value)
0275 except Exception, e:
0276 raise Invalid(str(e), {}, value, state)
0277 return result
0278
0279class Constant(FancyValidator):
0280
0281 """
0282 This converter converts everything to the same thing.
0283
0284 I.e., you pass in the constant value when initializing, then all
0285 values get converted to that constant value.
0286
0287 This is only really useful for funny situations, like::
0288
0289 fromEmailValidator = ValidateAny(
0290 ValidEmailAddress(),
0291 Constant('unknown@localhost'))
0292
0293 In this case, the if the email is not valid
0294 ``'unknown@localhost'`` will be used instead. Of course, you
0295 could use ``if_invalid`` instead.
0296
0297 Examples::
0298
0299 >>> Constant('X').to_python('y')
0300 'X'
0301 """
0302
0303 __unpackargs__ = ('value',)
0304
0305 def _to_python(self, value, state):
0306 return self.value
0307
0308 _from_python = _to_python
0309
0310############################################################
0311## Normal validators
0312############################################################
0313
0314class MaxLength(FancyValidator):
0315
0316 """
0317 Invalid if the value is longer than `maxLength`. Uses len(),
0318 so it can work for strings, lists, or anything with length.
0319
0320 Examples::
0321
0322 >>> max5 = MaxLength(5)
0323 >>> max5.to_python('12345')
0324 '12345'
0325 >>> max5.from_python('12345')
0326 '12345'
0327 >>> max5.to_python('123456')
0328 Traceback (most recent call last):
0329 ...
0330 Invalid: Enter a value less than 5 characters long
0331 >>> max5(accept_python=False).from_python('123456')
0332 Traceback (most recent call last):
0333 ...
0334 Invalid: Enter a value less than 5 characters long
0335 >>> max5.to_python([1, 2, 3])
0336 [1, 2, 3]
0337 >>> max5.to_python([1, 2, 3, 4, 5, 6])
0338 Traceback (most recent call last):
0339 ...
0340 Invalid: Enter a value less than 5 characters long
0341 >>> max5.to_python(5)
0342 Traceback (most recent call last):
0343 ...
0344 Invalid: Invalid value (value with length expected)
0345 """
0346
0347 __unpackargs__ = ('maxLength',)
0348 messages = {
0349 'tooLong': _("Enter a value less than %(maxLength)i characters long"),
0350 'invalid': _("Invalid value (value with length expected)"),
0351 }
0352
0353 def validate_python(self, value, state):
0354 try:
0355 if value and len(value) > self.maxLength:
0357 raise Invalid(self.message('tooLong', state,
0358 maxLength=self.maxLength),
0359 value, state)
0360 else:
0361 return None
0362 except TypeError:
0363 raise Invalid(self.message('invalid', state),
0364 value, state)
0365
0366class MinLength(FancyValidator):
0367
0368 """
0369 Invalid if the value is shorter than `minlength`. Uses len(),
0370 so it can work for strings, lists, or anything with length.
0371
0372 Examples::
0373
0374 >>> min5 = MinLength(5)
0375 >>> min5.to_python('12345')
0376 '12345'
0377 >>> min5.from_python('12345')
0378 '12345'
0379 >>> min5.to_python('1234')
0380 Traceback (most recent call last):
0381 ...
0382 Invalid: Enter a value at least 5 characters long
0383 >>> min5(accept_python=False).from_python('1234')
0384 Traceback (most recent call last):
0385 ...
0386 Invalid: Enter a value at least 5 characters long
0387 >>> min5.to_python([1, 2, 3, 4, 5])
0388 [1, 2, 3, 4, 5]
0389 >>> min5.to_python([1, 2, 3])
0390 Traceback (most recent call last):
0391 ...
0392 Invalid: Enter a value at least 5 characters long
0393 >>> min5.to_python(5)
0394 Traceback (most recent call last):
0395 ...
0396 Invalid: Invalid value (value with length expected)
0397
0398 """
0399
0400 __unpackargs__ = ('minLength',)
0401
0402 messages = {
0403 'tooShort': _("Enter a value at least %(minLength)i characters long"),
0404 'invalid': _("Invalid value (value with length expected)"),
0405 }
0406
0407 def validate_python(self, value, state):
0408 try:
0409 if len(value) < self.minLength:
0410 raise Invalid(self.message('tooShort', state,
0411 minLength=self.minLength),
0412 value, state)
0413 except TypeError:
0414 raise Invalid(self.message('invalid', state),
0415 value, state)
0416
0417class NotEmpty(FancyValidator):
0418
0419 """
0420 Invalid if value is empty (empty string, empty list, etc).
0421
0422 Generally for objects that Python considers false, except zero
0423 which is not considered invalid.
0424
0425 Examples::
0426
0427 >>> ne = NotEmpty(messages={'empty': 'enter something'})
0428 >>> ne.to_python('')
0429 Traceback (most recent call last):
0430 ...
0431 Invalid: enter something
0432 >>> ne.to_python(0)
0433 0
0434 """
0435 not_empty = True
0436
0437 messages = {
0438 'empty': _("Please enter a value"),
0439 }
0440
0441 def validate_python(self, value, state):
0442 if value == 0:
0443 # This isn't "empty" for this definition.
0444 return value
0445 if not value:
0446 raise Invalid(self.message('empty', state),
0447 value, state)
0448
0449class Empty(FancyValidator):
0450
0451 """
0452 Invalid unless the value is empty. Use cleverly, if at all.
0453
0454 Examples::
0455
0456 >>> Empty.to_python(0)
0457 Traceback (most recent call last):
0458 ...
0459 Invalid: You cannot enter a value here
0460 """
0461
0462 messages = {
0463 'notEmpty': _("You cannot enter a value here"),
0464 }
0465
0466 def validate_python(self, value, state):
0467 if value or value == 0:
0468 raise Invalid(self.message('notEmpty', state),
0469 value, state)
0470
0471class Regex(FancyValidator):
0472
0473 """
0474 Invalid if the value doesn't match the regular expression `regex`.
0475
0476 The regular expression can be a compiled re object, or a string
0477 which will be compiled for you.
0478
0479 Use strip=True if you want to strip the value before validation,
0480 and as a form of conversion (often useful).
0481
0482 Examples::
0483
0484 >>> cap = Regex(r'^[A-Z]+$')
0485 >>> cap.to_python('ABC')
0486 'ABC'
0487
0488 Note that ``.from_python()`` calls (in general) do not validate
0489 the input::
0490
0491 >>> cap.from_python('abc')
0492 'abc'
0493 >>> cap(accept_python=False).from_python('abc')
0494 Traceback (most recent call last):
0495 ...
0496 Invalid: The input is not valid
0497 >>> cap.to_python(1)
0498 Traceback (most recent call last):
0499 ...
0500 Invalid: The input must be a string (not a <type 'int'>: 1)
0501 >>> Regex(r'^[A-Z]+$', strip=True).to_python(' ABC ')
0502 'ABC'
0503 >>> Regex(r'this', regexOps=('I',)).to_python('THIS')
0504 'THIS'
0505 """
0506
0507 regexOps = ()
0508 strip = False
0509 regex = None
0510
0511 __unpackargs__ = ('regex',)
0512
0513 messages = {
0514 'invalid': _("The input is not valid"),
0515 }
0516
0517 def __init__(self, *args, **kw):
0518 FancyValidator.__init__(self, *args, **kw)
0519 if isinstance(self.regex, basestring):
0520 ops = 0
0521 assert not isinstance(self.regexOps, basestring), (
0522 "regexOps should be a list of options from the re module "
0523 "(names, or actual values)")
0524 for op in self.regexOps:
0525 if isinstance(op, basestring):
0526 ops |= getattr(re, op)
0527 else:
0528 ops |= op
0529 self.regex = re.compile(self.regex, ops)
0530
0531 def validate_python(self, value, state):
0532 self.assert_string(value, state)
0533 if self.strip and isinstance(value, basestring):
0534 value = value.strip()
0535 if not self.regex.search(value):
0536 raise Invalid(self.message('invalid', state),
0537 value, state)
0538
0539 def _to_python(self, value, state):
0540 if self.strip and isinstance(value, basestring):
0541 return value.strip()
0542 return value
0543
0544class PlainText(Regex):
0545
0546 """
0547 Test that the field contains only letters, numbers, underscore,
0548 and the hyphen. Subclasses Regex.
0549
0550 Examples::
0551
0552 >>> PlainText.to_python('_this9_')
0553 '_this9_'
0554 >>> PlainText.from_python(' this ')
0555 ' this '
0556 >>> PlainText(accept_python=False).from_python(' this ')
0557 Traceback (most recent call last):
0558 ...
0559 Invalid: Enter only letters, numbers, or _ (underscore)
0560 >>> PlainText(strip=True).to_python(' this ')
0561 'this'
0562 >>> PlainText(strip=True).from_python(' this ')
0563 'this'
0564 """
0565
0566 regex = r"^[a-zA-Z_\-0-9]*$"
0567
0568 messages = {
0569 'invalid': _('Enter only letters, numbers, or _ (underscore)'),
0570 }
0571
0572class OneOf(FancyValidator):
0573
0574 """
0575 Tests that the value is one of the members of a given list.
0576
0577 If ``testValueLists=True``, then if the input value is a list or
0578 tuple, all the members of the sequence will be checked (i.e., the
0579 input must be a subset of the allowed values).