2
Core classes for validation.
10
from pkg_resources import resource_filename
12
resource_filename = None
14
__all__ = ['NoDefault', 'Invalid', 'Validator', 'Identity',
15
'FancyValidator', 'is_validator']
21
Retrieve the location of locales.
23
If we're built as an egg, we need to find the resource within the egg.
24
Otherwise, we need to look for the locales on the filesystem or in the
25
system message catalog.
29
if resource_filename is not None:
31
locale_dir = resource_filename(__name__, "/i18n")
32
except NotImplementedError:
33
# resource_filename doesn't work with non-egg zip files
35
if not hasattr(os, 'access'):
36
# This happens on Google App Engine
37
return os.path.join(os.path.dirname(__file__), 'i18n')
38
if os.access(locale_dir, os.R_OK | os.X_OK):
39
# If the resource is present in the egg, use it
42
# Otherwise, search the filesystem
43
locale_dir = os.path.join(os.path.dirname(__file__), 'i18n')
44
if not os.access(locale_dir, os.R_OK | os.X_OK):
45
# Fallback on the system catalog
46
locale_dir = os.path.normpath('/usr/share/locale')
50
def set_stdtranslation(domain="FormEncode", languages=None, \
51
localedir = get_localedir()):
53
t = gettext.translation(domain=domain, \
54
languages=languages, \
55
localedir=localedir, fallback=True)
57
_stdtrans = t.ugettext
61
def _(s): return s # dummy i18n translation function, nothing is translated here.
62
# Instead this is actually done in api.Validator.message.
63
# The surrounding _("string") of the strings is only for extracting
64
# the strings automatically
65
# if you run pygettext with this source comment this function out temporarly
71
return (isinstance(obj, Validator) or
72
(isinstance(obj, type) and
73
issubclass(obj, Validator)))
75
class Invalid(Exception):
78
This is raised in response to invalid input. It has several
82
The message, *without* values substituted. For instance, if
83
you want HTML quoting of values, you can apply that.
85
The arguments (a dictionary) to go with `msg`.
87
The message describing the error, with values substituted.
89
The offending (invalid) value.
91
The state that went with this validator. This is an
92
application-specific object.
94
If this was a compound validator that takes a repeating value,
95
and sub-validator(s) had errors, then this is a list of those
96
exceptions. The list will be the same length as the number of
97
values -- valid values will have None instead of an exception.
99
Like `error_list`, but for dictionary compound validators.
102
def __init__(self, msg,
103
value, state, error_list=None, error_dict=None):
104
Exception.__init__(self, msg)
108
self.error_list = error_list
109
self.error_dict = error_dict
110
assert (not self.error_list or not self.error_dict), (
111
"Errors shouldn't have both error dicts and lists "
112
"(error %s has %s and %s)"
113
% (self, self.error_list, self.error_dict))
118
# val += " (value: %s)" % repr(self.value)
121
def __unicode__(self):
122
if isinstance(self.msg, unicode):
124
elif isinstance(self.msg, str):
125
return self.msg.decode('utf8')
127
return unicode(self.msg)
129
def unpack_errors(self, encode_variables=False, dict_char='.',
132
Returns the error as a simple data structure -- lists,
133
dictionaries, and strings.
135
If ``encode_variables`` is true, then this will return a flat
136
dictionary, encoded with variable_encode
139
assert not encode_variables, (
140
"You can only encode dictionary errors")
141
assert not self.error_dict
143
for item in self.error_list:
147
result.append(item.unpack_errors())
149
elif self.error_dict:
151
for name, item in self.error_dict.items():
152
if isinstance(item, (str, unicode)):
155
result[name] = item.unpack_errors()
157
import variabledecode
158
result = variabledecode.variable_encode(result, add_repetitions=False,
161
for key in result.keys():
166
assert not encode_variables, (
167
"You can only encode dictionary errors")
171
############################################################
173
############################################################
175
class Validator(declarative.Declarative):
178
The base class of most validators. See `IValidator` for more, and
179
`FancyValidator` for the more common (and more featureful) class.
183
if_missing = NoDefault
187
use_builtins_gettext = True #In case you dont want to use __builtins__._
188
#altough it may be definied, set this to False
190
__singletonmethods__ = ('to_python', 'from_python', 'message', 'all_messages',
193
def __classinit__(cls, new_attrs):
194
if new_attrs.has_key('messages'):
195
cls._messages = cls._messages.copy()
196
cls._messages.update(cls.messages)
198
cls._initialize_docstring()
200
def __init__(self, *args, **kw):
201
if kw.has_key('messages'):
202
self._messages = self._messages.copy()
203
self._messages.update(kw['messages'])
205
declarative.Declarative.__init__(self, *args, **kw)
207
def to_python(self, value, state=None):
210
def from_python(self, value, state=None):
213
def message(self, msgName, state, **kw):
214
#determine translation function
217
except AttributeError:
219
if self.use_builtins_gettext:
221
trans = __builtin__._
226
except AttributeError:
229
if not callable(trans):
233
msg = self._messages[msgName]
234
msg = trans(msg, **self.gettextargs)
239
"Key not found (%s) for %r=%r %% %r (from: %s)"
240
% (e, msgName, self._messages.get(msgName), kw,
241
', '.join(self._messages.keys())))
243
def all_messages(self):
245
Return a dictionary of all the messages of this validator, and
246
any subvalidators if present. Keys are message names, values
247
may be a message or list of messages. This is really just
248
intended for documentation purposes, to show someone all the
249
messages that a validator or compound validator (like Schemas)
252
@@: Should this produce a more structured set of messages, so
253
that messages could be unpacked into a rendered form to see
254
the placement of all the messages? Well, probably so.
256
msgs = self._messages.copy()
257
for v in self.subvalidators():
258
inner = v.all_messages()
259
for key, msg in inner:
263
if isinstance(msgs[key], list):
264
msgs[key].append(msg)
266
msgs[key] = [msgs[key], msg]
271
def subvalidators(self):
273
Return any validators that this validator contains. This is
274
not useful for functional, except to inspect what values are
275
available. Specifically the ``.all_messages()`` method uses
276
this to accumulate all possible messages.
281
def _initialize_docstring(cls):
283
This changes the class's docstring to include information
284
about all the messages this validator uses.
286
doc = cls.__doc__ or ''
287
doc = [textwrap.dedent(doc).rstrip()]
288
messages = cls._messages.items()
290
doc.append('\n\n**Messages**\n\n')
291
for name, default in messages:
292
default = re.sub(r'(%\(.*?\)[rsifcx])', r'``\1``', default)
293
doc.append('``'+name+'``:\n')
294
doc.append(' '+default+'\n\n')
295
cls.__doc__ = ''.join(doc)
296
_initialize_docstring = classmethod(_initialize_docstring)
298
class _Identity(Validator):
300
return 'validators.Identity'
301
Identity = _Identity()
303
class FancyValidator(Validator):
306
FancyValidator is the (abstract) superclass for various validators
307
and converters. A subclass can validate, convert, or do both.
308
There is no formal distinction made here.
310
Validators have two important external methods:
312
* .to_python(value, state):
313
Attempts to convert the value. If there is a problem, or the
314
value is not valid, an Invalid exception is raised. The
315
argument for this exception is the (potentially HTML-formatted)
316
error message to give the user.
318
* .from_python(value, state):
321
There are five important methods for subclasses to override,
322
however none of these *have* to be overridden, only the ones that
323
are appropriate for the validator:
326
if the `declarative.Declarative` model doesn't work for this.
328
* .validate_python(value, state):
329
This should raise an error if necessary. The value is a Python
330
object, either the result of to_python, or the input to
333
* .validate_other(value, state):
334
Validates the source, before to_python, or after from_python.
335
It's more common to use `.validate_python()` however.
337
* ._to_python(value, state):
338
This returns the converted value, or raises an Invalid
339
exception if there is an error. The argument to this exception
340
should be the error message.
342
* ._from_python(value, state):
343
Should undo .to_python() in some reasonable way, returning
346
Validators should have no internal state besides the
347
values given at instantiation. They should be reusable and
350
All subclasses can take the arguments/instance variables:
353
If set, then this value will be returned if the input evaluates
354
to false (empty list, empty string, None, etc), but not the 0 or
355
False objects. This only applies to ``.to_python()``.
358
If true, then if an empty value is given raise an error.
359
(Both with ``.to_python()`` and also ``.from_python()``
360
if ``.validate_python`` is true).
363
If true and the input is a string, strip it (occurs before empty
367
If set, then when this validator would raise Invalid during
368
``.to_python()``, instead return this value.
371
If set, when the Python value (converted with
372
``.from_python()``) is invalid, this value will be returned.
375
If True (the default), then ``.validate_python()`` and
376
``.validate_other()`` will not be called when
377
``.from_python()`` is used.
380
if_invalid = NoDefault
381
if_invalid_python = NoDefault
388
'empty': _("Please enter a value"),
389
'badType': _("The input must be a string (not a %(type)s: %(value)r)"),
390
'noneType': _("The input must be a string (not None)"),
393
def to_python(self, value, state=None):
395
if self.strip and isinstance(value, (str, unicode)):
396
value = value.strip()
397
elif hasattr(value, 'mixed'):
398
# Support Paste's MultiDict
399
value = value.mixed()
400
if self.is_empty(value):
402
raise Invalid(self.message('empty', state), value, state)
404
if self.if_empty is not NoDefault:
407
return self.empty_value(value)
408
vo = self.validate_other
409
if vo and vo is not self._validate_noop:
413
value = tp(value, state)
414
vp = self.validate_python
415
if vp and vp is not self._validate_noop:
419
if self.if_invalid is NoDefault:
422
return self.if_invalid
424
def from_python(self, value, state=None):
426
if self.strip and isinstance(value, (str, unicode)):
427
value = value.strip()
428
if not self.accept_python:
429
if self.is_empty(value):
431
raise Invalid(self.message('empty', state),
434
return self.empty_value(value)
435
vp = self.validate_python
436
if vp and vp is not self._validate_noop:
438
fp = self._from_python
440
value = fp(value, state)
441
vo = self.validate_other
442
if vo and vo is not self._validate_noop:
446
if self.is_empty(value):
447
return self.empty_value(value)
448
fp = self._from_python
450
value = self._from_python(value, state)
453
if self.if_invalid_python is NoDefault:
456
return self.if_invalid_python
458
def is_empty(self, value):
459
# None and '' are "empty"
460
return value is None or value == '' or (
461
isinstance(value, (list, tuple, dict)) and not value)
463
def empty_value(self, value):
466
def assert_string(self, value, state):
467
if not isinstance(value, (str, unicode)):
468
raise Invalid(self.message('badType', state,
469
type=type(value), value=value),
472
def base64encode(self, value):
474
Encode a string in base64, stripping whitespace and removing
477
return value.encode('base64').strip().replace('\n', '')
479
def _validate_noop(self, value, state):
481
A validation method that doesn't do anything.
485
validate_python = validate_other = _validate_noop