App Engine Python SDK version 1.9.2
[gae.git] / python / google / appengine / api / validation.py
blob0ffc3f89f701ed3d9afd3c57b2ce1226bc964b37
1 #!/usr/bin/env python
3 # Copyright 2007 Google Inc.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
21 """Validation tools for generic object structures.
23 This library is used for defining classes with constrained attributes.
24 Attributes are defined on the class which contains them using validators.
25 Although validators can be defined by any client of this library, a number
26 of standard validators are provided here.
28 Validators can be any callable that takes a single parameter which checks
29 the new value before it is assigned to the attribute. Validators are
30 permitted to modify a received value so that it is appropriate for the
31 attribute definition. For example, using int as a validator will cast
32 a correctly formatted string to a number, or raise an exception if it
33 can not. This is not recommended, however. the correct way to use a
34 validator that ensure the correct type is to use the Type validator.
36 This validation library is mainly intended for use with the YAML object
37 builder. See yaml_object.py.
38 """
48 import re
50 import google
51 import yaml
54 class Error(Exception):
55 """Base class for all package errors."""
58 class AttributeDefinitionError(Error):
59 """An error occurred in the definition of class attributes."""
62 class ValidationError(Error):
63 """Base class for raising exceptions during validation."""
65 def __init__(self, message, cause=None):
66 """Initialize exception."""
67 if hasattr(cause, 'args') and cause.args:
68 Error.__init__(self, message, *cause.args)
69 else:
72 Error.__init__(self, message)
73 self.message = message
74 self.cause = cause
76 def __str__(self):
77 return str(self.message)
80 class MissingAttribute(ValidationError):
81 """Raised when a required attribute is missing from object."""
84 def AsValidator(validator):
85 """Wrap various types as instances of a validator.
87 Used to allow shorthand for common validator types. It
88 converts the following types to the following Validators.
90 strings -> Regex
91 type -> Type
92 collection -> Options
93 Validator -> Its self!
95 Args:
96 validator: Object to wrap in a validator.
98 Returns:
99 Validator instance that wraps the given value.
101 Raises:
102 AttributeDefinitionError: if validator is not one of the above described
103 types.
105 if isinstance(validator, (str, unicode)):
106 return Regex(validator, type(validator))
107 if isinstance(validator, type):
108 return Type(validator)
109 if isinstance(validator, (list, tuple, set)):
110 return Options(*tuple(validator))
111 if isinstance(validator, Validator):
112 return validator
113 else:
114 raise AttributeDefinitionError('%s is not a valid validator' %
115 str(validator))
118 def _SimplifiedValue(validator, value):
119 """Convert any value to simplified collections and basic types.
121 Args:
122 validator: An instance of Validator that corresponds with 'value'.
123 May also be 'str' or 'int' if those were used instead of a full
124 Validator.
125 value: Value to convert to simplified collections.
127 Returns:
128 The value as a dictionary if it is a ValidatedBase object. A list of
129 items converted to simplified collections if value is a list
130 or a tuple. Otherwise, just the value.
132 if isinstance(value, ValidatedBase):
134 return value.ToDict()
135 elif isinstance(value, (list, tuple)):
137 return [_SimplifiedValue(validator, item) for item in value]
138 elif isinstance(validator, Validator):
139 return validator.ToValue(value)
140 return value
143 class ValidatedBase(object):
144 """Base class for all validated objects."""
146 @classmethod
147 def GetValidator(self, key):
148 """Safely get the Validator corresponding to the given key.
150 This function should be overridden by subclasses
152 Args:
153 key: The attribute or item to get a validator for.
155 Returns:
156 Validator associated with key or attribute.
158 Raises:
159 ValidationError: if the requested key is illegal.
161 raise NotImplementedError('Subclasses of ValidatedBase must '
162 'override GetValidator.')
164 def SetMultiple(self, attributes):
165 """Set multiple values on Validated instance.
167 All attributes will be validated before being set.
169 Args:
170 attributes: A dict of attributes/items to set.
172 Raises:
173 ValidationError: when no validated attribute exists on class.
175 for key, value in attributes.iteritems():
176 self.Set(key, value)
178 def Set(self, key, value):
179 """Set a single value on Validated instance.
181 This method should be overridded by sub-classes.
183 This method can only be used to assign validated attributes/items.
185 Args:
186 key: The name of the attributes
187 value: The value to set
189 Raises:
190 ValidationError: when no validated attribute exists on class.
192 raise NotImplementedError('Subclasses of ValidatedBase must override Set.')
194 def CheckInitialized(self):
195 """Checks that all required fields are initialized.
197 This function is called after all attributes have been checked to
198 verify any higher level constraints, for example ensuring all required
199 attributes are present.
201 Subclasses should override this function and raise an exception for
202 any errors.
204 pass
206 def ToDict(self):
207 """Convert ValidatedBase object to a dictionary.
209 Recursively traverses all of its elements and converts everything to
210 simplified collections.
212 Subclasses should override this method.
214 Returns:
215 A dictionary mapping all attributes to simple values or collections.
217 raise NotImplementedError('Subclasses of ValidatedBase must '
218 'override ToDict.')
220 def ToYAML(self):
221 """Print validated object as simplified YAML.
223 Returns:
224 Object as a simplified YAML string compatible with parsing using the
225 SafeLoader.
227 return yaml.dump(self.ToDict(),
228 default_flow_style=False,
229 Dumper=yaml.SafeDumper)
232 class Validated(ValidatedBase):
233 """Base class for classes that require validation.
235 A class which intends to use validated fields should sub-class itself from
236 this class. Each class should define an 'ATTRIBUTES' class variable which
237 should be a map from attribute name to its validator. For example:
239 class Story(Validated):
240 ATTRIBUTES = {'title': Type(str),
241 'authors': Repeated(Type(str)),
242 'isbn': Optional(Type(str)),
243 'pages': Type(int),
246 Attributes that are not listed under ATTRIBUTES work like normal and are
247 not validated upon assignment.
251 ATTRIBUTES = None
253 def __init__(self, **attributes):
254 """Constructor for Validated classes.
256 This constructor can optionally assign values to the class via its
257 keyword arguments.
259 Raises:
260 AttributeDefinitionError: when class instance is missing ATTRIBUTE
261 definition or when ATTRIBUTE is of the wrong type.
263 if not isinstance(self.ATTRIBUTES, dict):
264 raise AttributeDefinitionError(
265 'The class %s does not define an ATTRIBUTE variable.'
266 % self.__class__)
269 for key in self.ATTRIBUTES.keys():
270 object.__setattr__(self, key, self.GetValidator(key).default)
272 self.SetMultiple(attributes)
274 @classmethod
275 def GetValidator(self, key):
276 """Safely get the underlying attribute definition as a Validator.
278 Args:
279 key: Name of attribute to get.
281 Returns:
282 Validator associated with key or attribute value wrapped in a
283 validator.
285 if key not in self.ATTRIBUTES:
286 raise ValidationError(
287 'Unexpected attribute \'%s\' for object of type %s.' %
288 (key, self.__name__))
290 return AsValidator(self.ATTRIBUTES[key])
292 def Set(self, key, value):
293 """Set a single value on Validated instance.
295 This method can only be used to assign validated attributes.
297 Args:
298 key: The name of the attributes
299 value: The value to set
301 Raises:
302 ValidationError when no validated attribute exists on class.
304 setattr(self, key, value)
306 def Get(self, key):
307 """Get a single value on Validated instance.
309 This method can only be used to retrieve validated attributes.
311 Args:
312 key: The name of the attributes
314 Raises:
315 ValidationError when no validated attribute exists on class.
317 self.GetValidator(key)
318 return getattr(self, key)
320 def CheckInitialized(self):
321 """Checks that all required fields are initialized.
323 Since an instance of Validated starts off in an uninitialized state, it
324 is sometimes necessary to check that it has been fully initialized.
325 The main problem this solves is how to validate that an instance has
326 all of its required fields set. By default, Validator classes do not
327 allow None, but all attributes are initialized to None when instantiated.
329 Raises:
330 Exception relevant to the kind of validation. The type of the exception
331 is determined by the validator. Typically this will be ValueError or
332 TypeError.
334 for key in self.ATTRIBUTES.iterkeys():
335 try:
336 self.GetValidator(key)(getattr(self, key))
337 except MissingAttribute, e:
338 e.message = "Missing required value '%s'." % key
339 raise e
341 def __setattr__(self, key, value):
342 """Set attribute.
344 Setting a value on an object of this type will only work for attributes
345 defined in ATTRIBUTES. To make other assignments possible it is necessary
346 to override this method in subclasses.
348 It is important that assignment is restricted in this way because
349 this validation is used as validation for parsing. Absent this restriction
350 it would be possible for method names to be overwritten.
352 Args:
353 key: Name of attribute to set.
354 value: Attributes new value.
356 Raises:
357 ValidationError: when trying to assign to an attribute
358 that does not exist.
360 value = self.GetValidator(key)(value, key)
361 object.__setattr__(self, key, value)
363 def __str__(self):
364 """Formatted view of validated object and nested values."""
365 return repr(self)
367 def __repr__(self):
368 """Formatted view of validated object and nested values."""
369 values = [(attr, getattr(self, attr)) for attr in self.ATTRIBUTES]
370 dent = ' '
371 value_list = []
372 for attr, value in values:
373 value_list.append('\n%s%s=%s' % (dent, attr, value))
375 return "<%s %s\n%s>" % (self.__class__.__name__, ' '.join(value_list), dent)
377 def __eq__(self, other):
378 """Equality operator.
380 Comparison is done by comparing all attribute values to those in the other
381 instance. Objects which are not of the same type are not equal.
383 Args:
384 other: Other object to compare against.
386 Returns:
387 True if validated objects are equal, else False.
389 if type(self) != type(other):
390 return False
391 for key in self.ATTRIBUTES.iterkeys():
392 if getattr(self, key) != getattr(other, key):
393 return False
394 return True
396 def __ne__(self, other):
397 """Inequality operator."""
398 return not self.__eq__(other)
400 def __hash__(self):
401 """Hash function for using Validated objects in sets and maps.
403 Hash is done by hashing all keys and values and xor'ing them together.
405 Returns:
406 Hash of validated object.
408 result = 0
409 for key in self.ATTRIBUTES.iterkeys():
410 value = getattr(self, key)
411 if isinstance(value, list):
412 value = tuple(value)
413 result = result ^ hash(key) ^ hash(value)
414 return result
416 def ToDict(self):
417 """Convert Validated object to a dictionary.
419 Recursively traverses all of its elements and converts everything to
420 simplified collections.
422 Returns:
423 A dict of all attributes defined in this classes ATTRIBUTES mapped
424 to its value. This structure is recursive in that Validated objects
425 that are referenced by this object and in lists are also converted to
426 dicts.
428 result = {}
429 for name, validator in self.ATTRIBUTES.iteritems():
430 value = getattr(self, name)
432 if not(isinstance(validator, Validator) and value == validator.default):
433 result[name] = _SimplifiedValue(validator, value)
434 return result
437 class ValidatedDict(ValidatedBase, dict):
438 """Base class for validated dictionaries.
440 You can control the keys and values that are allowed in the dictionary
441 by setting KEY_VALIDATOR and VALUE_VALIDATOR to subclasses of Validator (or
442 things that can be interpreted as validators, see AsValidator).
444 For example if you wanted only capitalized keys that map to integers
445 you could do:
447 class CapitalizedIntegerDict(ValidatedDict):
448 KEY_VALIDATOR = Regex('[A-Z].*')
449 VALUE_VALIDATOR = int # this gets interpreted to Type(int)
451 The following code would result in an error:
453 my_dict = CapitalizedIntegerDict()
454 my_dict['lowercase'] = 5 # Throws a validation exception
456 You can freely nest Validated and ValidatedDict inside each other so:
458 class MasterObject(Validated):
459 ATTRIBUTES = {'paramdict': CapitalizedIntegerDict}
461 Could be used to parse the following yaml:
462 paramdict:
463 ArbitraryKey: 323
464 AnotherArbitraryKey: 9931
466 KEY_VALIDATOR = None
467 VALUE_VALIDATOR = None
469 def __init__(self, **kwds):
470 """Construct a validated dict by interpreting the key and value validators.
472 Args:
473 **kwds: keyword arguments will be validated and put into the dict.
475 self.update(kwds)
477 @classmethod
478 def GetValidator(self, key):
479 """Check the key for validity and return a corresponding value validator.
481 Args:
482 key: The key that will correspond to the validator we are returning.
484 key = AsValidator(self.KEY_VALIDATOR)(key, 'key in %s' % self.__name__)
485 return AsValidator(self.VALUE_VALIDATOR)
487 def __setitem__(self, key, value):
488 """Set an item.
490 Only attributes accepted by GetValidator and values that validate
491 with the validator returned from GetValidator are allowed to be set
492 in this dictionary.
494 Args:
495 key: Name of item to set.
496 value: Items new value.
498 Raises:
499 ValidationError: when trying to assign to a value that does not exist.
501 dict.__setitem__(self, key, self.GetValidator(key)(value, key))
503 def setdefault(self, key, value=None):
504 """Trap setdefaultss to ensure all key/value pairs are valid.
506 See the documentation for setdefault on dict for usage details.
508 Raises:
509 ValidationError: if the specified key is illegal or the
510 value invalid.
512 return dict.setdefault(self, key, self.GetValidator(key)(value, key))
514 def update(self, other, **kwds):
515 """Trap updates to ensure all key/value pairs are valid.
517 See the documentation for update on dict for usage details.
519 Raises:
520 ValidationError: if any of the specified keys are illegal or
521 values invalid.
523 if hasattr(other, 'keys') and callable(getattr(other, 'keys')):
524 newother = {}
525 for k in other:
526 newother[k] = self.GetValidator(k)(other[k], k)
527 else:
528 newother = [(k, self.GetValidator(k)(v, k)) for (k, v) in other]
530 newkwds = {}
531 for k in kwds:
532 newkwds[k] = self.GetValidator(k)(kwds[k], k)
534 dict.update(self, newother, **newkwds)
536 def Set(self, key, value):
537 """Set a single value on Validated instance.
539 This method checks that a given key and value are valid and if so
540 puts the item into this dictionary.
542 Args:
543 key: The name of the attributes
544 value: The value to set
546 Raises:
547 ValidationError: when no validated attribute exists on class.
549 self[key] = value
551 def ToDict(self):
552 """Convert ValidatedBase object to a dictionary.
554 Recursively traverses all of its elements and converts everything to
555 simplified collections.
557 Subclasses should override this method.
559 Returns:
560 A dictionary mapping all attributes to simple values or collections.
562 result = {}
563 for name, value in self.iteritems():
564 validator = self.GetValidator(name)
565 result[name] = _SimplifiedValue(validator, value)
566 return result
572 class Validator(object):
573 """Validator base class.
575 Though any callable can be used as a validator, this class encapsulates the
576 case when a specific validator needs to hold a particular state or
577 configuration.
579 To implement Validator sub-class, override the validate method.
581 This class is permitted to change the ultimate value that is set to the
582 attribute if there is a reasonable way to perform the conversion.
585 expected_type = object
587 def __init__(self, default=None):
588 """Constructor.
590 Args:
591 default: Default assignment is made during initialization and will
592 not pass through validation.
594 self.default = default
596 def __call__(self, value, key='???'):
597 """Main interface to validator is call mechanism."""
598 return self.Validate(value, key)
600 def Validate(self, value, key='???'):
601 """Override this method to customize sub-class behavior.
603 Args:
604 value: Value to validate.
605 key: Name of the field being validated.
607 Returns:
608 Value if value is valid, or a valid representation of value.
610 return value
612 def ToValue(self, value):
613 """Convert 'value' to a simplified collection or basic type.
615 Subclasses of Validator should override this method when the dumped
616 representation of 'value' is not simply <type>(value) (e.g. a regex).
618 Args:
619 value: An object of the same type that was returned from Validate().
621 Returns:
622 An instance of a builtin type (e.g. int, str, dict, etc). By default
623 it returns 'value' unmodified.
625 return value
628 class Type(Validator):
629 """Verifies property is of expected type.
631 Can optionally convert value if it is not of the expected type.
633 It is possible to specify a required field of a specific type in shorthand
634 by merely providing the type. This method is slightly less efficient than
635 providing an explicit type but is not significant unless parsing a large
636 amount of information:
638 class Person(Validated):
639 ATTRIBUTES = {'name': unicode,
640 'age': int,
643 However, in most instances it is best to use the type constants:
645 class Person(Validated):
646 ATTRIBUTES = {'name': TypeUnicode,
647 'age': TypeInt,
651 def __init__(self, expected_type, convert=True, default=None):
652 """Initialize Type validator.
654 Args:
655 expected_type: Type that attribute should validate against.
656 convert: Cause conversion if value is not the right type.
657 Conversion is done by calling the constructor of the type
658 with the value as its first parameter.
659 default: Default assignment is made during initialization and will
660 not pass through validation.
662 super(Type, self).__init__(default)
663 self.expected_type = expected_type
664 self.convert = convert
666 def Validate(self, value, key):
667 """Validate that value has the correct type.
669 Args:
670 value: Value to validate.
671 key: Name of the field being validated.
673 Returns:
674 value if value is of the correct type. value is coverted to the correct
675 type if the Validator is configured to do so.
677 Raises:
678 MissingAttribute: if value is None and the expected type is not NoneType.
679 ValidationError: if value is not of the right type and the validator
680 is either configured not to convert or cannot convert.
682 if not isinstance(value, self.expected_type):
683 if value is None:
684 raise MissingAttribute('Missing value is required.')
686 if self.convert:
687 try:
688 return self.expected_type(value)
689 except ValueError, e:
690 raise ValidationError(
691 'Value %r for %s could not be converted to type %s.' % (
692 value, key, self.expected_type.__name__), e)
693 except TypeError, e:
694 raise ValidationError(
695 'Value %r for %s is not of the expected type %s' % (
696 value, key, self.expected_type.__name__), e)
697 else:
698 raise ValidationError(
699 'Value %r for %s is not of the expected type %s' % (
700 value, key, self.expected_type.__name__))
701 else:
702 return value
705 TYPE_BOOL = Type(bool)
706 TYPE_INT = Type(int)
707 TYPE_LONG = Type(long)
708 TYPE_STR = Type(str)
709 TYPE_UNICODE = Type(unicode)
710 TYPE_FLOAT = Type(float)
713 class Options(Validator):
714 """Limit field based on pre-determined values.
716 Options are used to make sure an enumerated set of values are the only
717 one permitted for assignment. It is possible to define aliases which
718 map multiple string values to a single original. An example of usage:
720 class ZooAnimal(validated.Class):
721 ATTRIBUTES = {
722 'name': str,
723 'kind': Options('platypus', # No aliases
724 ('rhinoceros', ['rhino']), # One alias
725 ('canine', ('dog', 'puppy')), # Two aliases
729 def __init__(self, *options, **kw):
730 """Initialize options.
732 Args:
733 options: List of allowed values.
735 if 'default' in kw:
736 default = kw['default']
737 else:
738 default = None
740 alias_map = {}
741 def AddAlias(alias, original):
742 """Set new alias on alias_map.
744 Raises:
745 AttributeDefinitionError: when option already exists or if alias is
746 not of type str.
748 if not isinstance(alias, str):
749 raise AttributeDefinitionError(
750 'All option values must be of type str.')
751 elif alias in alias_map:
752 raise AttributeDefinitionError(
753 "Option '%s' already defined for options property." % alias)
754 alias_map[alias] = original
757 for option in options:
758 if isinstance(option, str):
759 AddAlias(option, option)
761 elif isinstance(option, (list, tuple)):
763 if len(option) != 2:
764 raise AttributeDefinitionError("Alias is defined as a list of tuple "
765 "with two items. The first is the "
766 "original option, while the second "
767 "is a list or tuple of str aliases.\n"
768 "\n Example:\n"
769 " ('original', ('alias1', "
770 "'alias2'")
771 original, aliases = option
772 AddAlias(original, original)
773 if not isinstance(aliases, (list, tuple)):
774 raise AttributeDefinitionError('Alias lists must be a list or tuple')
776 for alias in aliases:
777 AddAlias(alias, original)
779 else:
780 raise AttributeDefinitionError("All options must be of type str "
781 "or of the form (str, [str...]).")
782 super(Options, self).__init__(default)
783 self.options = alias_map
785 def Validate(self, value, key):
786 """Validate options.
788 Returns:
789 Original value for provided alias.
791 Raises:
792 ValidationError: when value is not one of predefined values.
794 if value is None:
795 raise ValidationError('Value for options field must not be None.')
796 value = str(value)
797 if value not in self.options:
798 raise ValidationError('Value \'%s\' for %s not in %s.'
799 % (value, key, self.options))
800 return self.options[value]
803 class Optional(Validator):
804 """Definition of optional attributes.
806 Optional values are attributes which can be set to None or left
807 unset. All values in a basic Validated class are set to None
808 at initialization. Failure to assign to non-optional values
809 will result in a validation error when calling CheckInitialized.
812 def __init__(self, validator, default=None):
813 """Initializer.
815 This constructor will make a few guesses about the value passed in
816 as the validator:
818 - If the validator argument is a type, it automatically creates a Type
819 validator around it.
821 - If the validator argument is a list or tuple, it automatically
822 creates an Options validator around it.
824 Args:
825 validator: Optional validation condition.
827 Raises:
828 AttributeDefinitionError: if validator is not callable.
830 self.validator = AsValidator(validator)
831 self.expected_type = self.validator.expected_type
832 self.default = default
834 def Validate(self, value, key):
835 """Optionally require a value.
837 Normal validators do not accept None. This will accept none on
838 behalf of the contained validator.
840 Args:
841 value: Value to be validated as optional.
842 key: Name of the field being validated.
844 Returns:
845 None if value is None, else results of contained validation.
847 if value is None:
848 return None
849 return self.validator(value, key)
851 def ToValue(self, value):
852 """Convert 'value' to a simplified collection or basic type."""
853 if value is None:
854 return None
855 return self.validator.ToValue(value)
858 class Regex(Validator):
859 """Regular expression validator.
861 Regular expression validator always converts value to string. Note that
862 matches must be exact. Partial matches will not validate. For example:
864 class ClassDescr(Validated):
865 ATTRIBUTES = { 'name': Regex(r'[a-zA-Z_][a-zA-Z_0-9]*'),
866 'parent': Type(type),
869 Alternatively, any attribute that is defined as a string is automatically
870 interpreted to be of type Regex. It is possible to specify unicode regex
871 strings as well. This approach is slightly less efficient, but usually
872 is not significant unless parsing large amounts of data:
874 class ClassDescr(Validated):
875 ATTRIBUTES = { 'name': r'[a-zA-Z_][a-zA-Z_0-9]*',
876 'parent': Type(type),
879 # This will raise a ValidationError exception.
880 my_class(name='AName with space', parent=AnotherClass)
883 def __init__(self, regex, string_type=unicode, default=None):
884 """Initialized regex validator.
886 Args:
887 regex: Regular expression string to use for comparison.
889 Raises:
890 AttributeDefinitionError: if string_type is not a kind of string.
892 super(Regex, self).__init__(default)
893 if (not issubclass(string_type, basestring) or
894 string_type is basestring):
895 raise AttributeDefinitionError(
896 'Regex fields must be a string type not %s.' % str(string_type))
897 if isinstance(regex, basestring):
898 self.re = re.compile('^(?:%s)$' % regex)
899 else:
900 raise AttributeDefinitionError(
901 'Regular expression must be string. Found %s.' % str(regex))
903 self.expected_type = string_type
905 def Validate(self, value, key):
906 """Does validation of a string against a regular expression.
908 Args:
909 value: String to match against regular expression.
910 key: Name of the field being validated.
912 Raises:
913 ValidationError: when value does not match regular expression or
914 when value does not match provided string type.
916 if issubclass(self.expected_type, str):
917 cast_value = TYPE_STR(value)
918 else:
919 cast_value = TYPE_UNICODE(value)
921 if self.re.match(cast_value) is None:
922 raise ValidationError('Value \'%s\' for %s does not match expression '
923 '\'%s\'' % (value, key, self.re.pattern))
924 return cast_value
927 class _RegexStrValue(object):
928 """Simulates the regex object to support recompilation when necessary.
930 Used by the RegexStr class to dynamically build and recompile regular
931 expression attributes of a validated object. This object replaces the normal
932 object returned from re.compile which is immutable.
934 When the value of this object is a string, that string is simply used as the
935 regular expression when recompilation is needed. If the state of this object
936 is a list of strings, the strings are joined in to a single 'or' expression.
939 def __init__(self, attribute, value, key):
940 """Initialize recompilable regex value.
942 Args:
943 attribute: Attribute validator associated with this regex value.
944 value: Initial underlying python value for regex string. Either a single
945 regex string or a list of regex strings.
946 key: Name of the field.
948 self.__attribute = attribute
949 self.__value = value
950 self.__regex = None
951 self.__key = key
953 def __AsString(self, value):
954 """Convert a value to appropriate string.
956 Returns:
957 String version of value with all carriage returns and line feeds removed.
959 if issubclass(self.__attribute.expected_type, str):
960 cast_value = TYPE_STR(value)
961 else:
962 cast_value = TYPE_UNICODE(value)
964 cast_value = cast_value.replace('\n', '')
965 cast_value = cast_value.replace('\r', '')
966 return cast_value
968 def __BuildRegex(self):
969 """Build regex string from state.
971 Returns:
972 String version of regular expression. Sequence objects are constructed
973 as larger regular expression where each regex in the list is joined with
974 all the others as single 'or' expression.
976 if isinstance(self.__value, list):
977 value_list = self.__value
978 sequence = True
979 else:
980 value_list = [self.__value]
981 sequence = False
983 regex_list = []
984 for item in value_list:
985 regex_list.append(self.__AsString(item))
987 if sequence:
988 return '|'.join('%s' % item for item in regex_list)
989 else:
990 return regex_list[0]
992 def __Compile(self):
993 """Build regular expression object from state.
995 Returns:
996 Compiled regular expression based on internal value.
998 regex = self.__BuildRegex()
999 try:
1000 return re.compile(regex)
1001 except re.error, e:
1002 raise ValidationError('Value \'%s\' for %s does not compile: %s' %
1003 (regex, self.__key, e), e)
1005 @property
1006 def regex(self):
1007 """Compiled regular expression as described by underlying value."""
1008 return self.__Compile()
1010 def match(self, value):
1011 """Match against internal regular expression.
1013 Returns:
1014 Regular expression object built from underlying value.
1016 return re.match(self.__BuildRegex(), value)
1018 def Validate(self):
1019 """Ensure that regex string compiles."""
1020 self.__Compile()
1022 def __str__(self):
1023 """Regular expression string as described by underlying value."""
1024 return self.__BuildRegex()
1026 def __eq__(self, other):
1027 """Comparison against other regular expression string values."""
1028 if isinstance(other, _RegexStrValue):
1029 return self.__BuildRegex() == other.__BuildRegex()
1030 return str(self) == other
1032 def __ne__(self, other):
1033 """Inequality operator for regular expression string value."""
1034 return not self.__eq__(other)
1037 class RegexStr(Validator):
1038 """Validates that a string can compile as a regex without errors.
1040 Use this validator when the value of a field should be a regex. That
1041 means that the value must be a string that can be compiled by re.compile().
1042 The attribute will then be a compiled re object.
1045 def __init__(self, string_type=unicode, default=None):
1046 """Initialized regex validator.
1048 Raises:
1049 AttributeDefinitionError: if string_type is not a kind of string.
1051 if default is not None:
1052 default = _RegexStrValue(self, default, None)
1053 re.compile(str(default))
1054 super(RegexStr, self).__init__(default)
1055 if (not issubclass(string_type, basestring) or
1056 string_type is basestring):
1057 raise AttributeDefinitionError(
1058 'RegexStr fields must be a string type not %s.' % str(string_type))
1060 self.expected_type = string_type
1062 def Validate(self, value, key):
1063 """Validates that the string compiles as a regular expression.
1065 Because the regular expression might have been expressed as a multiline
1066 string, this function also strips newlines out of value.
1068 Args:
1069 value: String to compile as a regular expression.
1070 key: Name of the field being validated.
1072 Raises:
1073 ValueError when value does not compile as a regular expression. TypeError
1074 when value does not match provided string type.
1076 if isinstance(value, _RegexStrValue):
1077 return value
1078 value = _RegexStrValue(self, value, key)
1079 value.Validate()
1080 return value
1082 def ToValue(self, value):
1083 """Returns the RE pattern for this validator."""
1084 return str(value)
1087 class Range(Validator):
1088 """Validates that numbers fall within the correct range.
1090 In theory this class can be emulated using Options, however error
1091 messages generated from that class will not be very intelligible.
1092 This class essentially does the same thing, but knows the intended
1093 integer range.
1095 Also, this range class supports floats and other types that implement
1096 ordinality.
1098 The range is inclusive, meaning 3 is considered in the range
1099 in Range(1,3).
1102 def __init__(self, minimum, maximum, range_type=int, default=None):
1103 """Initializer for range.
1105 Args:
1106 minimum: Minimum for attribute.
1107 maximum: Maximum for attribute.
1108 range_type: Type of field. Defaults to int.
1110 super(Range, self).__init__(default)
1111 if not isinstance(minimum, range_type):
1112 raise AttributeDefinitionError(
1113 'Minimum value must be of type %s, instead it is %s (%s).' %
1114 (str(range_type), str(type(minimum)), str(minimum)))
1115 if not isinstance(maximum, range_type):
1116 raise AttributeDefinitionError(
1117 'Maximum value must be of type %s, instead it is %s (%s).' %
1118 (str(range_type), str(type(maximum)), str(maximum)))
1120 self.minimum = minimum
1121 self.maximum = maximum
1122 self.expected_type = range_type
1123 self._type_validator = Type(range_type)
1125 def Validate(self, value, key):
1126 """Validate that value is within range.
1128 Validates against range-type then checks the range.
1130 Args:
1131 value: Value to validate.
1132 key: Name of the field being validated.
1134 Raises:
1135 ValidationError: when value is out of range. ValidationError when value
1136 is notd of the same range type.
1138 cast_value = self._type_validator.Validate(value, key)
1139 if cast_value < self.minimum or cast_value > self.maximum:
1140 raise ValidationError('Value \'%s\' for %s is out of range %s - %s'
1141 % (str(value),
1142 key,
1143 str(self.minimum),
1144 str(self.maximum)))
1145 return cast_value
1148 class Repeated(Validator):
1149 """Repeated field validator.
1151 Indicates that attribute is expected to be a repeated value, ie,
1152 a sequence. This adds additional validation over just Type(list)
1153 in that it retains information about what can be stored in the list by
1154 use of its constructor field.
1157 def __init__(self, constructor, default=None):
1158 """Initializer for repeated field.
1160 Args:
1161 constructor: Type used for verifying elements of sequence attribute.
1163 super(Repeated, self).__init__(default)
1164 self.constructor = constructor
1165 self.expected_type = list
1167 def Validate(self, value, key):
1168 """Do validation of sequence.
1170 Value must be a list and all elements must be of type 'constructor'.
1172 Args:
1173 value: Value to validate.
1174 key: Name of the field being validated.
1176 Raises:
1177 ValidationError: if value is None, not a list or one of its elements is
1178 the wrong type.
1180 if not isinstance(value, list):
1181 raise ValidationError('Value \'%s\' for %s should be a sequence but '
1182 'is not.' % (value, key))
1184 for item in value:
1185 if isinstance(self.constructor, Validator):
1186 item = self.constructor.Validate(item, key)
1187 elif not isinstance(item, self.constructor):
1188 raise ValidationError('Value element \'%s\' for %s must be type %s.' % (
1189 str(item), key, self.constructor.__name__))
1191 return value
1194 class TimeValue(Validator):
1195 """Validates time values with units, such as 1h or 3.5d."""
1197 _EXPECTED_SYNTAX = ('must be a non-negative number followed by a time unit, '
1198 'such as 1h or 3.5d')
1200 def __init__(self):
1201 super(TimeValue, self).__init__()
1202 self.expected_type = str
1204 def Validate(self, value, key):
1205 """Validate a time value.
1207 Args:
1208 value: Value to validate.
1209 key: Name of the field being validated.
1211 Raises:
1212 ValidationError: if value is not a time value with the expected format.
1214 if not isinstance(value, basestring):
1215 raise ValidationError("Value '%s' for %s is not a string (%s)"
1216 % (value, key, TimeValue._EXPECTED_SYNTAX))
1217 if not value:
1218 raise ValidationError("Value for %s is empty (%s)"
1219 % (key, TimeValue._EXPECTED_SYNTAX))
1220 if value[-1] not in "smhd":
1221 raise ValidationError("Value '%s' for %s must end with a time unit, "
1222 "one of s (seconds), m (minutes), h (hours), "
1223 "or d (days)" % (value, key))
1224 try:
1225 t = float(value[:-1])
1226 except ValueError:
1227 raise ValidationError("Value '%s' for %s is not a valid time value (%s)"
1228 % (value, key, TimeValue._EXPECTED_SYNTAX))
1229 if t < 0:
1230 raise ValidationError("Value '%s' for %s is negative (%s)"
1231 % (value, key, TimeValue._EXPECTED_SYNTAX))
1232 return value