Enhance valid_values to use __contains__
Actually valid_values doesn't cover many of the use cases or validates that the value and valid_values provided can be used with 'in' call. his patch tries to improve this situation to ensure that both are valid for being checked or fail otherwise plus validating that valid_values contains the value provided. Closes-Bug: 1604073 Change-Id: I47a6f7d58d6cbeb8fd245620d187c4c565497290
This commit is contained in:
parent
6cf9c87ef3
commit
abeb5c4de0
@ -93,19 +93,50 @@ def _validate_list_of_items(item_validator, data, *args, **kwargs):
|
||||
return msg
|
||||
|
||||
|
||||
def validate_values(data, valid_values=None):
|
||||
"""Validate a data value based on a list of valid values.
|
||||
def validate_values(data, valid_values=None, valid_values_display=None):
|
||||
"""Validate that the provided 'data' is within 'valid_values'.
|
||||
|
||||
:param data: The data value to validate.
|
||||
:param valid_values: A list (or sequence) of valid values data can be.
|
||||
:returns: None if data is in valid_values, otherwise a human readable
|
||||
message as to why the value is invalid.
|
||||
:param data: The data to check within valid_values.
|
||||
:param valid_values: A collection of values that 'data' must be in to be
|
||||
valid. The collection can be any type that supports the 'in' operation.
|
||||
:param valid_values_display: A string to display that describes the valid
|
||||
values. This string is only displayed when an invalid value is
|
||||
encountered.
|
||||
If no string is provided, the string "valid_values" will be used.
|
||||
:returns: The message to return if data not in valid_values.
|
||||
:raises: TypeError if the values for 'data' or 'valid_values' are not
|
||||
compatible for comparison or doesn't have __contains__.
|
||||
If TypeError is raised this is considered a programming error and the
|
||||
inputs (data) and (valid_values) must be checked so this is never
|
||||
raised on validation.
|
||||
"""
|
||||
|
||||
# If valid_values is not specified we don't check against it.
|
||||
if valid_values is None:
|
||||
return
|
||||
|
||||
# Check if we can use 'in' to find membership of data in valid_values
|
||||
contains = getattr(valid_values, "__contains__", None)
|
||||
if callable(contains):
|
||||
try:
|
||||
if data not in valid_values:
|
||||
msg = (_("'%(data)s' is not in %(valid_values)s") %
|
||||
{'data': data, 'valid_values': valid_values})
|
||||
valid_values_display = valid_values_display or 'valid_values'
|
||||
msg = (_("%(data)s is not in %(valid_values)s") %
|
||||
{'data': data, 'valid_values': valid_values_display})
|
||||
LOG.debug(msg)
|
||||
return msg
|
||||
except TypeError:
|
||||
# This is a programming error
|
||||
msg = (_("'data' of type '%(typedata)s' and 'valid_values'"
|
||||
"of type '%(typevalues)s' are not "
|
||||
"compatible for comparison") %
|
||||
{'typedata': type(data),
|
||||
'typevalues': type(valid_values)})
|
||||
raise TypeError(msg)
|
||||
else:
|
||||
# This is a programming error
|
||||
msg = (_("'valid_values' does not support membership operations"))
|
||||
raise TypeError(msg)
|
||||
|
||||
|
||||
def validate_not_empty_string_or_none(data, max_len=None):
|
||||
|
@ -94,17 +94,65 @@ class TestAttributeValidation(base.BaseTestCase):
|
||||
self.assertIs(validators.is_attr_set(data), True)
|
||||
|
||||
def test_validate_values(self):
|
||||
# Check that validation is not performed if valid_values is not set
|
||||
msg = validators.validate_values(4)
|
||||
self.assertIsNone(msg)
|
||||
|
||||
# Check that value is within valid_values
|
||||
msg = validators.validate_values(4, [4, 6])
|
||||
self.assertIsNone(msg)
|
||||
|
||||
# Check that value is within valid_values
|
||||
msg = validators.validate_values(4, (4, 6))
|
||||
self.assertIsNone(msg)
|
||||
|
||||
msg = validators.validate_values(7, [4, 6])
|
||||
self.assertEqual("'7' is not in [4, 6]", msg)
|
||||
# Check that value is within valid_values with strings
|
||||
msg = validators.validate_values("1", ["2", "1", "4", "5"])
|
||||
self.assertIsNone(msg)
|
||||
|
||||
msg = validators.validate_values(7, (4, 6))
|
||||
self.assertEqual("'7' is not in (4, 6)", msg)
|
||||
# Check that value is not compatible for comparision
|
||||
response = "'valid_values' does not support membership operations"
|
||||
self.assertRaisesRegex(TypeError, response,
|
||||
validators.validate_values, data=None,
|
||||
valid_values=True)
|
||||
|
||||
def test_validate_values_display(self):
|
||||
# Check that value is NOT within valid_values and report values
|
||||
msg = validators.validate_values(7, [4, 6],
|
||||
valid_values_display="[4, 6]")
|
||||
self.assertEqual("7 is not in [4, 6]", msg)
|
||||
|
||||
# Check that value is NOT within valid_values and report values
|
||||
msg = validators.validate_values(7, (4, 6),
|
||||
valid_values_display="(4, 6)")
|
||||
self.assertEqual("7 is not in (4, 6)", msg)
|
||||
|
||||
# Check values with a range function showing a custom string
|
||||
msg = validators.validate_values(8, range(8),
|
||||
valid_values_display="[0..7]")
|
||||
self.assertEqual("8 is not in [0..7]", msg)
|
||||
|
||||
# Check that value is not within valid_values and custom string
|
||||
msg = validators.validate_values(1, [2, 3, 4, 5],
|
||||
valid_values_display="[2, 3, 4, 5]")
|
||||
self.assertEqual("1 is not in [2, 3, 4, 5]", msg)
|
||||
|
||||
# Check that value is not within valid_values and custom string
|
||||
msg = validators.validate_values("1", ["2", "3", "4", "5"],
|
||||
valid_values_display="'valid_values"
|
||||
"_to_show'")
|
||||
self.assertEqual("1 is not in 'valid_values_to_show'", msg)
|
||||
|
||||
# Check that value is not comparable to valid_values and got Exception
|
||||
data = 1
|
||||
valid_values = '[2, 3, 4, 5]'
|
||||
response = "'data' of type '%s' and 'valid_values'of type" \
|
||||
" '%s' are not compatible for comparison" % (
|
||||
type(data), type(valid_values))
|
||||
self.assertRaisesRegex(TypeError, response,
|
||||
validators.validate_values, data,
|
||||
valid_values,
|
||||
valid_values_display="[2, 3, 4, 5]")
|
||||
|
||||
def test_validate_not_empty_string(self):
|
||||
msg = validators.validate_not_empty_string(' ', None)
|
||||
@ -203,7 +251,7 @@ class TestAttributeValidation(base.BaseTestCase):
|
||||
msg = validators.validate_integer(2, [2, 3, 4, 5])
|
||||
self.assertIsNone(msg)
|
||||
msg = validators.validate_integer(1, [2, 3, 4, 5])
|
||||
self.assertEqual("'1' is not in [2, 3, 4, 5]", msg)
|
||||
self.assertEqual("1 is not in valid_values", msg)
|
||||
|
||||
def test_validate_no_whitespace(self):
|
||||
data = 'no_white_space'
|
||||
|
Loading…
Reference in New Issue
Block a user