Added constraint checking
The checking requires some builtin type conversion to be more user friendly.
This commit is contained in:
parent
d2d1aafd93
commit
9c3f999879
@ -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):
|
||||||
|
Loading…
Reference in New Issue
Block a user