Added constraint checking

The checking requires some builtin type conversion to be more user
friendly.
This commit is contained in:
tengqm 2015-03-08 19:16:07 +08:00
parent d2d1aafd93
commit 9c3f999879

View File

@ -35,7 +35,7 @@ class AnyIndexDict(collections.Mapping):
return self.value return self.value
def __iter__(self): def __iter__(self):
return '*' yield '*'
def __len__(self): def __len__(self):
return 1 return 1
@ -68,11 +68,7 @@ class Schema(collections.Mapping):
'"%s"') % self[self.TYPE] '"%s"') % self[self.TYPE]
raise exception.InvalidSchemaError(message=msg) raise exception.InvalidSchemaError(message=msg)
if isinstance(schema, type(self)): if self[self.TYPE] == self.LIST:
if self[self.TYPE] != self.LIST:
msg = _('Single schema valid only for List, not '
'"%s"') % self[self.TYPE]
raise exception.InvalidSchemaError(message=msg)
self.schema = AnyIndexDict(schema) self.schema = AnyIndexDict(schema)
else: else:
self.schema = schema self.schema = schema
@ -116,35 +112,6 @@ class Schema(collections.Mapping):
for nested_schema in self.schema.values(): for nested_schema in self.schema.values():
nested_schema.validate(context) nested_schema.validate(context)
def _is_valid_constraint(self, constraint):
valid_types = getattr(constraint, 'valid_types', [])
return any(self.type == getattr(self, t, None) for t in valid_types)
def to_schema_type(self, value):
"""Returns the value in the schema's data type."""
try:
# We have to be backwards-compatible for Integer and Number
# Schema types and try to convert string representations of
# number into "real" number types, therefore calling
# str_to_num below.
if self.type == self.INTEGER:
num = Schema.str_to_num(value)
if isinstance(num, float):
raise ValueError(_('%s is not an integer.') % num)
return num
elif self.type == self.NUMBER:
return Schema.str_to_num(value)
elif self.type == self.STRING:
return str(value)
elif self.type == self.BOOLEAN:
return strutils.bool_from_string(str(value), strict=True)
except ValueError:
raise ValueError(_('Value "%(val)s" is invalid for data type '
'"%(type)s".')
% {'val': value, 'type': self.type})
return value
def validate_constraints(self, value, context=None, skipped=None): def validate_constraints(self, value, context=None, skipped=None):
if not skipped: if not skipped:
skipped = [] skipped = []
@ -152,9 +119,9 @@ class Schema(collections.Mapping):
try: try:
for constraint in self.constraints: for constraint in self.constraints:
if type(constraint) not in skipped: if type(constraint) not in skipped:
constraint.validate(value, self, context) constraint.validate(value, context)
except ValueError as ex: except ValueError as ex:
raise exception.StackValidationFailed(message=six.text_type(ex)) raise exception.SpecValidationFailed(message=six.text_type(ex))
def __getitem__(self, key): def __getitem__(self, key):
if key == self.DESCRIPTION: if key == self.DESCRIPTION:
@ -196,6 +163,9 @@ class Boolean(Schema):
else: else:
return super(Boolean, self).__getitem__(key) return super(Boolean, self).__getitem__(key)
def to_schema_type(self, value):
return strutils.bool_from_string(str(value), strict=True)
def resolve(self, value): def resolve(self, value):
if str(value).lower() not in ('true', 'false'): if str(value).lower() not in ('true', 'false'):
msg = _('The value "%s" is not a valid Boolean') % value msg = _('The value "%s" is not a valid Boolean') % value
@ -206,6 +176,7 @@ class Boolean(Schema):
def validate(self, value, context=None): def validate(self, value, context=None):
if isinstance(value, bool): if isinstance(value, bool):
return return
self.resolve(value) self.resolve(value)
@ -216,6 +187,16 @@ class Integer(Schema):
else: else:
return super(Integer, self).__getitem__(key) return super(Integer, self).__getitem__(key)
def to_schema_type(self, value):
if isinstance(value, (int, long)):
return value
try:
num = int(value)
except ValueError:
raise ValueError(_('%s is not an intger.') % num)
return num
def resolve(self, value): def resolve(self, value):
try: try:
return int(value) return int(value)
@ -225,9 +206,10 @@ class Integer(Schema):
raise exception.SpecValidationFailed(message=msg) raise exception.SpecValidationFailed(message=msg)
def validate(self, value, context=None): def validate(self, value, context=None):
if isinstance(value, (int, long)): if not isinstance(value, (int, long)):
return value = self.resolve(value)
self.resolve(value)
self.validate_constraints(value, self, context)
class String(Schema): class String(Schema):
@ -237,18 +219,23 @@ class String(Schema):
else: else:
return super(String, self).__getitem__(key) return super(String, self).__getitem__(key)
def to_schema_type(self, value):
return str(value)
def resolve(self, value): def resolve(self, value):
try: try:
return str(value) return str(value)
except (TypeError, ValueError) as ex: except (TypeError, ValueError) as ex:
raise ex raise ex
# raise exception.SpecValidationFailed(message=six.text_type(ex))
def validate(self, value, context=None): def validate(self, value, context=None):
if isinstance(value, six.string_types): if not isinstance(value, six.string_types):
return msg = _('The value "%s" cannot be converted into a '
'string.') % value
raise exception.SpecValidationFailed(message=msg)
self.resolve(value) self.resolve(value)
self.validate_constraints(value, self, context)
class Number(Schema): class Number(Schema):
@ -258,6 +245,15 @@ class Number(Schema):
else: else:
return super(Number, self).__getitem__(key) return super(Number, self).__getitem__(key)
def to_schema_type(self, value):
if isinstance(value, numbers.Number):
return value
try:
return int(value)
except ValueError:
return float(value)
def resolve(self, value): def resolve(self, value):
if isinstance(value, numbers.Number): if isinstance(value, numbers.Number):
return value return value
@ -272,6 +268,7 @@ class Number(Schema):
return return
self.resolve(value) self.resolve(value)
self.resolve_constraints(value, self, context)
class List(Schema): class List(Schema):