1.9.30 sync.
[gae.git] / python / google / appengine / api / validation.py
blob1a49f1e8558cd09655641156b082406181ef3852
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 """
49 import re
51 import google
52 import yaml
55 class Error(Exception):
56 """Base class for all package errors."""
59 class AttributeDefinitionError(Error):
60 """An error occurred in the definition of class attributes."""
63 class ValidationError(Error):
64 """Base class for raising exceptions during validation."""
66 def __init__(self, message, cause=None):
67 """Initialize exception."""
68 if hasattr(cause, 'args') and cause.args:
69 Error.__init__(self, message, *cause.args)
70 else:
73 Error.__init__(self, message)
74 self.message = message
75 self.cause = cause
77 def __str__(self):
78 return str(self.message)
81 class MissingAttribute(ValidationError):
82 """Raised when a required attribute is missing from object."""
85 def AsValidator(validator):
86 """Wrap various types as instances of a validator.
88 Used to allow shorthand for common validator types. It
89 converts the following types to the following Validators.
91 strings -> Regex
92 type -> Type
93 collection -> Options
94 Validator -> Its self!
96 Args:
97 validator: Object to wrap in a validator.
99 Returns:
100 Validator instance that wraps the given value.
102 Raises:
103 AttributeDefinitionError: if validator is not one of the above described
104 types.
106 if isinstance(validator, (str, unicode)):
107 return Regex(validator, type(validator))
108 if isinstance(validator, type):
109 return Type(validator)
110 if isinstance(validator, (list, tuple, set)):
111 return Options(*tuple(validator))
112 if isinstance(validator, Validator):
113 return validator
114 else:
115 raise AttributeDefinitionError('%s is not a valid validator' %
116 str(validator))
119 def _SimplifiedValue(validator, value):
120 """Convert any value to simplified collections and basic types.
122 Args:
123 validator: An instance of Validator that corresponds with 'value'.
124 May also be 'str' or 'int' if those were used instead of a full
125 Validator.
126 value: Value to convert to simplified collections.
128 Returns:
129 The value as a dictionary if it is a ValidatedBase object. A list of
130 items converted to simplified collections if value is a list
131 or a tuple. Otherwise, just the value.
133 if isinstance(value, ValidatedBase):
135 return value.ToDict()
136 elif isinstance(value, (list, tuple)):
138 return [_SimplifiedValue(validator, item) for item in value]
139 elif isinstance(validator, Validator):
140 return validator.ToValue(value)
141 return value
144 class ValidatedBase(object):
145 """Base class for all validated objects."""
147 @classmethod
148 def GetValidator(self, key):
149 """Safely get the Validator corresponding to the given key.
151 This function should be overridden by subclasses
153 Args:
154 key: The attribute or item to get a validator for.
156 Returns:
157 Validator associated with key or attribute.
159 Raises:
160 ValidationError: if the requested key is illegal.
162 raise NotImplementedError('Subclasses of ValidatedBase must '
163 'override GetValidator.')
165 def SetMultiple(self, attributes):
166 """Set multiple values on Validated instance.
168 All attributes will be validated before being set.
170 Args:
171 attributes: A dict of attributes/items to set.
173 Raises:
174 ValidationError: when no validated attribute exists on class.
176 for key, value in attributes.iteritems():
177 self.Set(key, value)
179 def Set(self, key, value):
180 """Set a single value on Validated instance.
182 This method should be overridded by sub-classes.
184 This method can only be used to assign validated attributes/items.
186 Args:
187 key: The name of the attributes
188 value: The value to set
190 Raises:
191 ValidationError: when no validated attribute exists on class.
193 raise NotImplementedError('Subclasses of ValidatedBase must override Set.')
195 def CheckInitialized(self):
196 """Checks that all required fields are initialized.
198 This function is called after all attributes have been checked to
199 verify any higher level constraints, for example ensuring all required
200 attributes are present.
202 Subclasses should override this function and raise an exception for
203 any errors.
205 pass
207 def ToDict(self):
208 """Convert ValidatedBase object to a dictionary.
210 Recursively traverses all of its elements and converts everything to
211 simplified collections.
213 Subclasses should override this method.
215 Returns:
216 A dictionary mapping all attributes to simple values or collections.
218 raise NotImplementedError('Subclasses of ValidatedBase must '
219 'override ToDict.')
221 def ToYAML(self):
222 """Print validated object as simplified YAML.
224 Returns:
225 Object as a simplified YAML string compatible with parsing using the
226 SafeLoader.
228 return yaml.dump(self.ToDict(),
229 default_flow_style=False,
230 Dumper=yaml.SafeDumper)
233 class Validated(ValidatedBase):
234 """Base class for classes that require validation.
236 A class which intends to use validated fields should sub-class itself from
237 this class. Each class should define an 'ATTRIBUTES' class variable which
238 should be a map from attribute name to its validator. For example:
240 class Story(Validated):
241 ATTRIBUTES = {'title': Type(str),
242 'authors': Repeated(Type(str)),
243 'isbn': Optional(Type(str)),
244 'pages': Type(int),
247 Attributes that are not listed under ATTRIBUTES work like normal and are
248 not validated upon assignment.
252 ATTRIBUTES = None
254 def __init__(self, **attributes):
255 """Constructor for Validated classes.
257 This constructor can optionally assign values to the class via its
258 keyword arguments.
260 Raises:
261 AttributeDefinitionError: when class instance is missing ATTRIBUTE
262 definition or when ATTRIBUTE is of the wrong type.
264 if not isinstance(self.ATTRIBUTES, dict):
265 raise AttributeDefinitionError(
266 'The class %s does not define an ATTRIBUTE variable.'
267 % self.__class__)
270 for key in self.ATTRIBUTES.keys():
271 object.__setattr__(self, key, self.GetValidator(key).default)
273 self.SetMultiple(attributes)
275 @classmethod
276 def GetValidator(self, key):
277 """Safely get the underlying attribute definition as a Validator.
279 Args:
280 key: Name of attribute to get.
282 Returns:
283 Validator associated with key or attribute value wrapped in a
284 validator.
286 if key not in self.ATTRIBUTES:
287 raise ValidationError(
288 'Unexpected attribute \'%s\' for object of type %s.' %
289 (key, self.__name__))
291 return AsValidator(self.ATTRIBUTES[key])
293 def Set(self, key, value):
294 """Set a single value on Validated instance.
296 This method can only be used to assign validated attributes.
298 Args:
299 key: The name of the attributes
300 value: The value to set
302 Raises:
303 ValidationError when no validated attribute exists on class.
305 setattr(self, key, value)
307 def Get(self, key):
308 """Get a single value on Validated instance.
310 This method can only be used to retrieve validated attributes.
312 Args:
313 key: The name of the attributes
315 Raises:
316 ValidationError when no validated attribute exists on class.
318 self.GetValidator(key)
319 return getattr(self, key)
321 def CheckInitialized(self):
322 """Checks that all required fields are initialized.
324 Since an instance of Validated starts off in an uninitialized state, it
325 is sometimes necessary to check that it has been fully initialized.
326 The main problem this solves is how to validate that an instance has
327 all of its required fields set. By default, Validator classes do not
328 allow None, but all attributes are initialized to None when instantiated.
330 Raises:
331 Exception relevant to the kind of validation. The type of the exception
332 is determined by the validator. Typically this will be ValueError or
333 TypeError.
335 for key in self.ATTRIBUTES.iterkeys():
336 try:
337 self.GetValidator(key)(getattr(self, key))
338 except MissingAttribute, e:
339 e.message = "Missing required value '%s'." % key
340 raise e
342 def __setattr__(self, key, value):
343 """Set attribute.
345 Setting a value on an object of this type will only work for attributes
346 defined in ATTRIBUTES. To make other assignments possible it is necessary
347 to override this method in subclasses.
349 It is important that assignment is restricted in this way because
350 this validation is used as validation for parsing. Absent this restriction
351 it would be possible for method names to be overwritten.
353 Args:
354 key: Name of attribute to set.
355 value: Attributes new value.
357 Raises:
358 ValidationError: when trying to assign to an attribute
359 that does not exist.
361 value = self.GetValidator(key)(value, key)
362 object.__setattr__(self, key, value)
364 def __str__(self):
365 """Formatted view of validated object and nested values."""
366 return repr(self)
368 def __repr__(self):
369 """Formatted view of validated object and nested values."""
370 values = [(attr, getattr(self, attr)) for attr in self.ATTRIBUTES]
371 dent = ' '
372 value_list = []
373 for attr, value in values:
374 value_list.append('\n%s%s=%s' % (dent, attr, value))
376 return "<%s %s\n%s>" % (self.__class__.__name__, ' '.join(value_list), dent)
378 def __eq__(self, other):
379 """Equality operator.
381 Comparison is done by comparing all attribute values to those in the other
382 instance. Objects which are not of the same type are not equal.
384 Args:
385 other: Other object to compare against.
387 Returns:
388 True if validated objects are equal, else False.
390 if type(self) != type(other):
391 return False
392 for key in self.ATTRIBUTES.iterkeys():
393 if getattr(self, key) != getattr(other, key):
394 return False
395 return True
397 def __ne__(self, other):
398 """Inequality operator."""
399 return not self.__eq__(other)
401 def __hash__(self):
402 """Hash function for using Validated objects in sets and maps.
404 Hash is done by hashing all keys and values and xor'ing them together.
406 Returns:
407 Hash of validated object.
409 result = 0
410 for key in self.ATTRIBUTES.iterkeys():
411 value = getattr(self, key)
412 if isinstance(value, list):
413 value = tuple(value)
414 result = result ^ hash(key) ^ hash(value)
415 return result
417 def ToDict(self):
418 """Convert Validated object to a dictionary.
420 Recursively traverses all of its elements and converts everything to
421 simplified collections.
423 Returns:
424 A dict of all attributes defined in this classes ATTRIBUTES mapped
425 to its value. This structure is recursive in that Validated objects
426 that are referenced by this object and in lists are also converted to
427 dicts.
429 result = {}
430 for name, validator in self.ATTRIBUTES.iteritems():
431 value = getattr(self, name)
433 if not(isinstance(validator, Validator) and value == validator.default):
434 result[name] = _SimplifiedValue(validator, value)
435 return result
438 class ValidatedDict(ValidatedBase, dict):
439 """Base class for validated dictionaries.
441 You can control the keys and values that are allowed in the dictionary
442 by setting KEY_VALIDATOR and VALUE_VALIDATOR to subclasses of Validator (or
443 things that can be interpreted as validators, see AsValidator).
445 For example if you wanted only capitalized keys that map to integers
446 you could do:
448 class CapitalizedIntegerDict(ValidatedDict):
449 KEY_VALIDATOR = Regex('[A-Z].*')
450 VALUE_VALIDATOR = int # this gets interpreted to Type(int)
452 The following code would result in an error:
454 my_dict = CapitalizedIntegerDict()
455 my_dict['lowercase'] = 5 # Throws a validation exception
457 You can freely nest Validated and ValidatedDict inside each other so:
459 class MasterObject(Validated):
460 ATTRIBUTES = {'paramdict': CapitalizedIntegerDict}
462 Could be used to parse the following yaml:
463 paramdict:
464 ArbitraryKey: 323
465 AnotherArbitraryKey: 9931
467 KEY_VALIDATOR = None
468 VALUE_VALIDATOR = None
470 def __init__(self, **kwds):
471 """Construct a validated dict by interpreting the key and value validators.
473 Args:
474 **kwds: keyword arguments will be validated and put into the dict.
476 self.update(kwds)
478 @classmethod
479 def GetValidator(self, key):
480 """Check the key for validity and return a corresponding value validator.
482 Args:
483 key: The key that will correspond to the validator we are returning.
485 key = AsValidator(self.KEY_VALIDATOR)(key, 'key in %s' % self.__name__)
486 return AsValidator(self.VALUE_VALIDATOR)
488 def __setitem__(self, key, value):
489 """Set an item.
491 Only attributes accepted by GetValidator and values that validate
492 with the validator returned from GetValidator are allowed to be set
493 in this dictionary.
495 Args:
496 key: Name of item to set.
497 value: Items new value.
499 Raises:
500 ValidationError: when trying to assign to a value that does not exist.
502 dict.__setitem__(self, key, self.GetValidator(key)(value, key))
504 def setdefault(self, key, value=None):
505 """Trap setdefaultss to ensure all key/value pairs are valid.
507 See the documentation for setdefault on dict for usage details.
509 Raises:
510 ValidationError: if the specified key is illegal or the
511 value invalid.
513 return dict.setdefault(self, key, self.GetValidator(key)(value, key))
515 def update(self, other, **kwds):
516 """Trap updates to ensure all key/value pairs are valid.
518 See the documentation for update on dict for usage details.
520 Raises:
521 ValidationError: if any of the specified keys are illegal or
522 values invalid.
524 if hasattr(other, 'keys') and callable(getattr(other, 'keys')):
525 newother = {}
526 for k in other:
527 newother[k] = self.GetValidator(k)(other[k], k)
528 else:
529 newother = [(k, self.GetValidator(k)(v, k)) for (k, v) in other]
531 newkwds = {}
532 for k in kwds:
533 newkwds[k] = self.GetValidator(k)(kwds[k], k)
535 dict.update(self, newother, **newkwds)
537 def Set(self, key, value):
538 """Set a single value on Validated instance.
540 This method checks that a given key and value are valid and if so
541 puts the item into this dictionary.
543 Args:
544 key: The name of the attributes
545 value: The value to set
547 Raises:
548 ValidationError: when no validated attribute exists on class.
550 self[key] = value
552 def ToDict(self):
553 """Convert ValidatedBase object to a dictionary.
555 Recursively traverses all of its elements and converts everything to
556 simplified collections.
558 Subclasses should override this method.
560 Returns:
561 A dictionary mapping all attributes to simple values or collections.
563 result = {}
564 for name, value in self.iteritems():
565 validator = self.GetValidator(name)
566 result[name] = _SimplifiedValue(validator, value)
567 return result
573 class Validator(object):
574 """Validator base class.
576 Though any callable can be used as a validator, this class encapsulates the
577 case when a specific validator needs to hold a particular state or
578 configuration.
580 To implement Validator sub-class, override the validate method.
582 This class is permitted to change the ultimate value that is set to the
583 attribute if there is a reasonable way to perform the conversion.
586 expected_type = object
588 def __init__(self, default=None):
589 """Constructor.
591 Args:
592 default: Default assignment is made during initialization and will
593 not pass through validation.
595 self.default = default
597 def __call__(self, value, key='???'):
598 """Main interface to validator is call mechanism."""
599 return self.Validate(value, key)
601 def Validate(self, value, key='???'):
602 """Override this method to customize sub-class behavior.
604 Args:
605 value: Value to validate.
606 key: Name of the field being validated.
608 Returns:
609 Value if value is valid, or a valid representation of value.
611 return value
613 def ToValue(self, value):
614 """Convert 'value' to a simplified collection or basic type.
616 Subclasses of Validator should override this method when the dumped
617 representation of 'value' is not simply <type>(value) (e.g. a regex).
619 Args:
620 value: An object of the same type that was returned from Validate().
622 Returns:
623 An instance of a builtin type (e.g. int, str, dict, etc). By default
624 it returns 'value' unmodified.
626 return value
629 class Type(Validator):
630 """Verifies property is of expected type.
632 Can optionally convert value if it is not of the expected type.
634 It is possible to specify a required field of a specific type in shorthand
635 by merely providing the type. This method is slightly less efficient than
636 providing an explicit type but is not significant unless parsing a large
637 amount of information:
639 class Person(Validated):
640 ATTRIBUTES = {'name': unicode,
641 'age': int,
644 However, in most instances it is best to use the type constants:
646 class Person(Validated):
647 ATTRIBUTES = {'name': TypeUnicode,
648 'age': TypeInt,
652 def __init__(self, expected_type, convert=True, default=None):
653 """Initialize Type validator.
655 Args:
656 expected_type: Type that attribute should validate against.
657 convert: Cause conversion if value is not the right type.
658 Conversion is done by calling the constructor of the type
659 with the value as its first parameter.
660 default: Default assignment is made during initialization and will
661 not pass through validation.
663 super(Type, self).__init__(default)
664 self.expected_type = expected_type
665 self.convert = convert
667 def Validate(self, value, key):
668 """Validate that value has the correct type.
670 Args:
671 value: Value to validate.
672 key: Name of the field being validated.
674 Returns:
675 value if value is of the correct type. value is coverted to the correct
676 type if the Validator is configured to do so.
678 Raises:
679 MissingAttribute: if value is None and the expected type is not NoneType.
680 ValidationError: if value is not of the right type and the validator
681 is either configured not to convert or cannot convert.
683 if not isinstance(value, self.expected_type):
684 if value is None:
685 raise MissingAttribute('Missing value is required.')
687 if self.convert:
688 try:
689 return self.expected_type(value)
690 except ValueError, e:
691 raise ValidationError(
692 'Value %r for %s could not be converted to type %s.' % (
693 value, key, self.expected_type.__name__), e)
694 except TypeError, e:
695 raise ValidationError(
696 'Value %r for %s is not of the expected type %s' % (
697 value, key, self.expected_type.__name__), e)
698 else:
699 raise ValidationError(
700 'Value %r for %s is not of the expected type %s' % (
701 value, key, self.expected_type.__name__))
702 else:
703 return value
706 TYPE_BOOL = Type(bool)
707 TYPE_INT = Type(int)
708 TYPE_LONG = Type(long)
709 TYPE_STR = Type(str)
710 TYPE_UNICODE = Type(unicode)
711 TYPE_FLOAT = Type(float)
714 class Options(Validator):
715 """Limit field based on pre-determined values.
717 Options are used to make sure an enumerated set of values are the only
718 one permitted for assignment. It is possible to define aliases which
719 map multiple string values to a single original. An example of usage:
721 class ZooAnimal(validated.Class):
722 ATTRIBUTES = {
723 'name': str,
724 'kind': Options('platypus', # No aliases
725 ('rhinoceros', ['rhino']), # One alias
726 ('canine', ('dog', 'puppy')), # Two aliases
730 def __init__(self, *options, **kw):
731 """Initialize options.
733 Args:
734 options: List of allowed values.
736 if 'default' in kw:
737 default = kw['default']
738 else:
739 default = None
741 alias_map = {}
742 def AddAlias(alias, original):
743 """Set new alias on alias_map.
745 Raises:
746 AttributeDefinitionError: when option already exists or if alias is
747 not of type str.
749 if not isinstance(alias, str):
750 raise AttributeDefinitionError(
751 'All option values must be of type str.')
752 elif alias in alias_map:
753 raise AttributeDefinitionError(
754 "Option '%s' already defined for options property." % alias)
755 alias_map[alias] = original
758 for option in options:
759 if isinstance(option, str):
760 AddAlias(option, option)
762 elif isinstance(option, (list, tuple)):
764 if len(option) != 2:
765 raise AttributeDefinitionError("Alias is defined as a list of tuple "
766 "with two items. The first is the "
767 "original option, while the second "
768 "is a list or tuple of str aliases.\n"
769 "\n Example:\n"
770 " ('original', ('alias1', "
771 "'alias2'")
772 original, aliases = option
773 AddAlias(original, original)
774 if not isinstance(aliases, (list, tuple)):
775 raise AttributeDefinitionError('Alias lists must be a list or tuple')
777 for alias in aliases:
778 AddAlias(alias, original)
780 else:
781 raise AttributeDefinitionError("All options must be of type str "
782 "or of the form (str, [str...]).")
783 super(Options, self).__init__(default)
784 self.options = alias_map
786 def Validate(self, value, key):
787 """Validate options.
789 Returns:
790 Original value for provided alias.
792 Raises:
793 ValidationError: when value is not one of predefined values.
795 if value is None:
796 raise ValidationError('Value for options field must not be None.')
797 value = str(value)
798 if value not in self.options:
799 raise ValidationError('Value \'%s\' for %s not in %s.'
800 % (value, key, self.options))
801 return self.options[value]
804 class Optional(Validator):
805 """Definition of optional attributes.
807 Optional values are attributes which can be set to None or left
808 unset. All values in a basic Validated class are set to None
809 at initialization. Failure to assign to non-optional values
810 will result in a validation error when calling CheckInitialized.
813 def __init__(self, validator, default=None):
814 """Initializer.
816 This constructor will make a few guesses about the value passed in
817 as the validator:
819 - If the validator argument is a type, it automatically creates a Type
820 validator around it.
822 - If the validator argument is a list or tuple, it automatically
823 creates an Options validator around it.
825 Args:
826 validator: Optional validation condition.
828 Raises:
829 AttributeDefinitionError: if validator is not callable.
831 self.validator = AsValidator(validator)
832 self.expected_type = self.validator.expected_type
833 self.default = default
835 def Validate(self, value, key):
836 """Optionally require a value.
838 Normal validators do not accept None. This will accept none on
839 behalf of the contained validator.
841 Args:
842 value: Value to be validated as optional.
843 key: Name of the field being validated.
845 Returns:
846 None if value is None, else results of contained validation.
848 if value is None:
849 return None
850 return self.validator(value, key)
852 def ToValue(self, value):
853 """Convert 'value' to a simplified collection or basic type."""
854 if value is None:
855 return None
856 return self.validator.ToValue(value)
859 class Regex(Validator):
860 """Regular expression validator.
862 Regular expression validator always converts value to string. Note that
863 matches must be exact. Partial matches will not validate. For example:
865 class ClassDescr(Validated):
866 ATTRIBUTES = { 'name': Regex(r'[a-zA-Z_][a-zA-Z_0-9]*'),
867 'parent': Type(type),
870 Alternatively, any attribute that is defined as a string is automatically
871 interpreted to be of type Regex. It is possible to specify unicode regex
872 strings as well. This approach is slightly less efficient, but usually
873 is not significant unless parsing large amounts of data:
875 class ClassDescr(Validated):
876 ATTRIBUTES = { 'name': r'[a-zA-Z_][a-zA-Z_0-9]*',
877 'parent': Type(type),
880 # This will raise a ValidationError exception.
881 my_class(name='AName with space', parent=AnotherClass)
884 def __init__(self, regex, string_type=unicode, default=None):
885 """Initialized regex validator.
887 Args:
888 regex: Regular expression string to use for comparison.
890 Raises:
891 AttributeDefinitionError: if string_type is not a kind of string.
893 super(Regex, self).__init__(default)
894 if (not issubclass(string_type, basestring) or
895 string_type is basestring):
896 raise AttributeDefinitionError(
897 'Regex fields must be a string type not %s.' % str(string_type))
898 if isinstance(regex, basestring):
899 self.re = re.compile('^(?:%s)$' % regex)
900 else:
901 raise AttributeDefinitionError(
902 'Regular expression must be string. Found %s.' % str(regex))
904 self.expected_type = string_type
906 def Validate(self, value, key):
907 """Does validation of a string against a regular expression.
909 Args:
910 value: String to match against regular expression.
911 key: Name of the field being validated.
913 Raises:
914 ValidationError: when value does not match regular expression or
915 when value does not match provided string type.
917 if issubclass(self.expected_type, str):
918 cast_value = TYPE_STR(value)
919 else:
920 cast_value = TYPE_UNICODE(value)
922 if self.re.match(cast_value) is None:
923 raise ValidationError('Value \'%s\' for %s does not match expression '
924 '\'%s\'' % (value, key, self.re.pattern))
925 return cast_value
928 class _RegexStrValue(object):
929 """Simulates the regex object to support recompilation when necessary.
931 Used by the RegexStr class to dynamically build and recompile regular
932 expression attributes of a validated object. This object replaces the normal
933 object returned from re.compile which is immutable.
935 When the value of this object is a string, that string is simply used as the
936 regular expression when recompilation is needed. If the state of this object
937 is a list of strings, the strings are joined in to a single 'or' expression.
940 def __init__(self, attribute, value, key):
941 """Initialize recompilable regex value.
943 Args:
944 attribute: Attribute validator associated with this regex value.
945 value: Initial underlying python value for regex string. Either a single
946 regex string or a list of regex strings.
947 key: Name of the field.
949 self.__attribute = attribute
950 self.__value = value
951 self.__regex = None
952 self.__key = key
954 def __AsString(self, value):
955 """Convert a value to appropriate string.
957 Returns:
958 String version of value with all carriage returns and line feeds removed.
960 if issubclass(self.__attribute.expected_type, str):
961 cast_value = TYPE_STR(value)
962 else:
963 cast_value = TYPE_UNICODE(value)
965 cast_value = cast_value.replace('\n', '')
966 cast_value = cast_value.replace('\r', '')
967 return cast_value
969 def __BuildRegex(self):
970 """Build regex string from state.
972 Returns:
973 String version of regular expression. Sequence objects are constructed
974 as larger regular expression where each regex in the list is joined with
975 all the others as single 'or' expression.
977 if isinstance(self.__value, list):
978 value_list = self.__value
979 sequence = True
980 else:
981 value_list = [self.__value]
982 sequence = False
984 regex_list = []
985 for item in value_list:
986 regex_list.append(self.__AsString(item))
988 if sequence:
989 return '|'.join('%s' % item for item in regex_list)
990 else:
991 return regex_list[0]
993 def __Compile(self):
994 """Build regular expression object from state.
996 Returns:
997 Compiled regular expression based on internal value.
999 regex = self.__BuildRegex()
1000 try:
1001 return re.compile(regex)
1002 except re.error, e:
1003 raise ValidationError('Value \'%s\' for %s does not compile: %s' %
1004 (regex, self.__key, e), e)
1006 @property
1007 def regex(self):
1008 """Compiled regular expression as described by underlying value."""
1009 return self.__Compile()
1011 def match(self, value):
1012 """Match against internal regular expression.
1014 Returns:
1015 Regular expression object built from underlying value.
1017 return re.match(self.__BuildRegex(), value)
1019 def Validate(self):
1020 """Ensure that regex string compiles."""
1021 self.__Compile()
1023 def __str__(self):
1024 """Regular expression string as described by underlying value."""
1025 return self.__BuildRegex()
1027 def __eq__(self, other):
1028 """Comparison against other regular expression string values."""
1029 if isinstance(other, _RegexStrValue):
1030 return self.__BuildRegex() == other.__BuildRegex()
1031 return str(self) == other
1033 def __ne__(self, other):
1034 """Inequality operator for regular expression string value."""
1035 return not self.__eq__(other)
1038 class RegexStr(Validator):
1039 """Validates that a string can compile as a regex without errors.
1041 Use this validator when the value of a field should be a regex. That
1042 means that the value must be a string that can be compiled by re.compile().
1043 The attribute will then be a compiled re object.
1046 def __init__(self, string_type=unicode, default=None):
1047 """Initialized regex validator.
1049 Raises:
1050 AttributeDefinitionError: if string_type is not a kind of string.
1052 if default is not None:
1053 default = _RegexStrValue(self, default, None)
1054 re.compile(str(default))
1055 super(RegexStr, self).__init__(default)
1056 if (not issubclass(string_type, basestring) or
1057 string_type is basestring):
1058 raise AttributeDefinitionError(
1059 'RegexStr fields must be a string type not %s.' % str(string_type))
1061 self.expected_type = string_type
1063 def Validate(self, value, key):
1064 """Validates that the string compiles as a regular expression.
1066 Because the regular expression might have been expressed as a multiline
1067 string, this function also strips newlines out of value.
1069 Args:
1070 value: String to compile as a regular expression.
1071 key: Name of the field being validated.
1073 Raises:
1074 ValueError when value does not compile as a regular expression. TypeError
1075 when value does not match provided string type.
1077 if isinstance(value, _RegexStrValue):
1078 return value
1079 value = _RegexStrValue(self, value, key)
1080 value.Validate()
1081 return value
1083 def ToValue(self, value):
1084 """Returns the RE pattern for this validator."""
1085 return str(value)
1088 class Range(Validator):
1089 """Validates that numbers fall within the correct range.
1091 In theory this class can be emulated using Options, however error
1092 messages generated from that class will not be very intelligible.
1093 This class essentially does the same thing, but knows the intended
1094 integer range.
1096 Also, this range class supports floats and other types that implement
1097 ordinality.
1099 The range is inclusive, meaning 3 is considered in the range
1100 in Range(1,3).
1103 def __init__(self, minimum, maximum, range_type=int, default=None):
1104 """Initializer for range.
1106 At least one of minimum and maximum must be supplied.
1108 Args:
1109 minimum: Minimum for attribute.
1110 maximum: Maximum for attribute.
1111 range_type: Type of field. Defaults to int.
1113 Raises:
1114 AttributeDefinitionError: if the specified parameters are incorrect.
1116 super(Range, self).__init__(default)
1117 if minimum is None and maximum is None:
1118 raise AttributeDefinitionError('Must specify minimum or maximum.')
1119 if minimum is not None and not isinstance(minimum, range_type):
1120 raise AttributeDefinitionError(
1121 'Minimum value must be of type %s, instead it is %s (%s).' %
1122 (str(range_type), str(type(minimum)), str(minimum)))
1123 if maximum is not None and not isinstance(maximum, range_type):
1124 raise AttributeDefinitionError(
1125 'Maximum value must be of type %s, instead it is %s (%s).' %
1126 (str(range_type), str(type(maximum)), str(maximum)))
1128 self.minimum = minimum
1129 self.maximum = maximum
1130 self.expected_type = range_type
1131 self._type_validator = Type(range_type)
1133 def Validate(self, value, key):
1134 """Validate that value is within range.
1136 Validates against range-type then checks the range.
1138 Args:
1139 value: Value to validate.
1140 key: Name of the field being validated.
1142 Raises:
1143 ValidationError: when value is out of range. ValidationError when value
1144 is not of the same range type.
1146 cast_value = self._type_validator.Validate(value, key)
1147 if self.maximum is None and cast_value < self.minimum:
1148 raise ValidationError('Value \'%s\' for %s less than %s'
1149 % (value, key, self.minimum))
1150 elif self.minimum is None and cast_value > self.maximum:
1151 raise ValidationError('Value \'%s\' for %s greater than %s'
1152 % (value, key, self.maximum))
1154 elif ((self.minimum is not None and cast_value < self.minimum) or
1155 (self.maximum is not None and cast_value > self.maximum)):
1156 raise ValidationError('Value \'%s\' for %s is out of range %s - %s'
1157 % (value, key, self.minimum, self.maximum))
1158 return cast_value
1161 class Repeated(Validator):
1162 """Repeated field validator.
1164 Indicates that attribute is expected to be a repeated value, ie,
1165 a sequence. This adds additional validation over just Type(list)
1166 in that it retains information about what can be stored in the list by
1167 use of its constructor field.
1170 def __init__(self, constructor, default=None):
1171 """Initializer for repeated field.
1173 Args:
1174 constructor: Type used for verifying elements of sequence attribute.
1176 super(Repeated, self).__init__(default)
1177 self.constructor = constructor
1178 self.expected_type = list
1180 def Validate(self, value, key):
1181 """Do validation of sequence.
1183 Value must be a list and all elements must be of type 'constructor'.
1185 Args:
1186 value: Value to validate.
1187 key: Name of the field being validated.
1189 Raises:
1190 ValidationError: if value is None, not a list or one of its elements is
1191 the wrong type.
1193 if not isinstance(value, list):
1194 raise ValidationError('Value \'%s\' for %s should be a sequence but '
1195 'is not.' % (value, key))
1197 for item in value:
1198 if isinstance(self.constructor, Validator):
1199 item = self.constructor.Validate(item, key)
1200 elif not isinstance(item, self.constructor):
1201 raise ValidationError('Value element \'%s\' for %s must be type %s.' % (
1202 str(item), key, self.constructor.__name__))
1204 return value
1207 class TimeValue(Validator):
1208 """Validates time values with units, such as 1h or 3.5d."""
1210 _EXPECTED_SYNTAX = ('must be a non-negative number followed by a time unit, '
1211 'such as 1h or 3.5d')
1213 def __init__(self):
1214 super(TimeValue, self).__init__()
1215 self.expected_type = str
1217 def Validate(self, value, key):
1218 """Validate a time value.
1220 Args:
1221 value: Value to validate.
1222 key: Name of the field being validated.
1224 Raises:
1225 ValidationError: if value is not a time value with the expected format.
1227 if not isinstance(value, basestring):
1228 raise ValidationError("Value '%s' for %s is not a string (%s)"
1229 % (value, key, TimeValue._EXPECTED_SYNTAX))
1230 if not value:
1231 raise ValidationError("Value for %s is empty (%s)"
1232 % (key, TimeValue._EXPECTED_SYNTAX))
1233 if value[-1] not in "smhd":
1234 raise ValidationError("Value '%s' for %s must end with a time unit, "
1235 "one of s (seconds), m (minutes), h (hours), "
1236 "or d (days)" % (value, key))
1237 try:
1238 t = float(value[:-1])
1239 except ValueError:
1240 raise ValidationError("Value '%s' for %s is not a valid time value (%s)"
1241 % (value, key, TimeValue._EXPECTED_SYNTAX))
1242 if t < 0:
1243 raise ValidationError("Value '%s' for %s is negative (%s)"
1244 % (value, key, TimeValue._EXPECTED_SYNTAX))
1245 return value