Implemented type args in config schema; added 'enum' and 'regex' types

Change-Id: I7ae8054aaca977ee4e74eace9ca4dce59834ec92
This commit is contained in:
Maxim Kulkin 2013-11-14 10:48:25 +04:00 committed by Oleg Gelbukh
parent aae261e036
commit e0c1b9d2aa
4 changed files with 60 additions and 19 deletions

View File

@ -132,7 +132,7 @@ class Configuration(object):
if param_schema:
type_validator = TypeValidatorRegistry.get_validator(
param_schema.type)
type_validation_result = type_validator.validate(value)
type_validation_result = type_validator.validate(value, **param_schema.type_args)
if not isinstance(type_validation_result, InvalidValueError):
value = type_validation_result
@ -159,7 +159,7 @@ class Configuration(object):
type_validator = TypeValidatorRegistry.get_validator(
param_schema.type)
type_validation_result = type_validator.validate(value)
type_validation_result = type_validator.validate(value, **param_schema.type_args)
if not isinstance(type_validation_result, InvalidValueError):
return None

View File

@ -65,6 +65,7 @@ class ConfigSchemaRegistry:
param = ConfigParameterSchema(
name, param_data['type'], section=section,
type_args=param_data.get('type_args', {}),
default=param_data.get('default', None),
description=param_data.get('help', None),
required=param_data.get('required', False),
@ -127,11 +128,12 @@ class ConfigSchema:
class ConfigParameterSchema:
def __init__(self, name, type, section=None, description=None,
def __init__(self, name, type, type_args={}, section=None, description=None,
default=None, required=False, deprecation_message=None):
self.section = section
self.section = section or 'DEFAULT'
self.name = name
self.type = type
self.type_args = type_args
self.fullname = param_fullname(name, section)
self.description = description
self.default = default
@ -183,10 +185,10 @@ class TypeValidator(object):
self.base_type = base_type
self.f = f
def validate(self, value):
def validate(self, value, **kwargs):
if value is None:
return value
return getattr(self, 'f')(value)
return getattr(self, 'f')(value, **kwargs)
def type_validator(name, base_type=None, default=False, **kwargs):
@ -194,8 +196,8 @@ def type_validator(name, base_type=None, default=False, **kwargs):
base_type = name
def wrap(fn):
def wrapped(s):
return fn(s, **kwargs)
def wrapped(s, **immediate_kwargs):
return fn(s, **dict(kwargs, **immediate_kwargs))
o = TypeValidator(base_type, wrapped)
TypeValidatorRegistry.register_validator(name, o, default=default)
return fn
@ -221,16 +223,19 @@ def validate_boolean(s):
return InvalidValueError('Value should be "true" or "false"')
@type_validator('enum')
def validate_enum(s, values=[]):
if s in values:
return None
if len(values) == 0:
message = 'There should be no value'
message = 'There should be no value, but found %s' % repr(s)
elif len(values) == 1:
message = 'The only valid value is %s' % values[0]
message = 'The only valid value is "%s", but found "%s"' % (
repr(values[0]), repr(s))
else:
message = 'Valid values are %s and %s' % (
', '.join(values[:-1]), values[-1])
message = 'Valid values are %s and %s, but found %s' % (
', '.join([repr(v) for v in values[:-1]]),
repr(values[-1]), repr(s))
return InvalidValueError('%s' % message)
@ -390,12 +395,22 @@ def validate_host_and_port(s, default_port=None):
@type_validator('multi', base_type='multi')
@type_validator('file', base_type='string')
@type_validator('directory', base_type='string')
@type_validator('regex', base_type='string')
@type_validator('host_v6', base_type='string')
def validate_string(s):
return s
@type_validator('regex', base_type='string')
@type_validator('regexp', base_type='string')
def validate_regex(s):
try:
re.compile(s)
except re.error as e:
return InvalidValueError(str(e))
return s
@type_validator('integer')
def validate_integer(s, min=None, max=None):
if isinstance(s, int):

View File

@ -118,7 +118,8 @@ def generate_project_schema(project):
param['type'] = prev_param['type']
if param.get('default', None) is not None:
value = validator.validate(param['default'])
type_args = param.get('type_args', {})
value = validator.validate(param['default'], **type_args)
if not isinstance(value, Issue):
param['default'] = value
else:
@ -217,7 +218,8 @@ def generate_project_schema(project):
param['default'] = old_param['default']
if param.get('default', None) is not None:
value = validator.validate(old_param['default'])
type_args = old_param.get('type_args', {})
value = validator.validate(old_param['default'], **type_args)
if not isinstance(value, Issue):
param['default'] = value
else:

View File

@ -9,12 +9,13 @@ class TypeValidatorTestHelper(object):
super(TypeValidatorTestHelper, self).setUp()
self.validator = TypeValidatorRegistry.get_validator(self.type_name)
def assertValid(self, value):
self.assertNotIsInstance(self.validator.validate(value), Issue)
def assertValid(self, value, type_args={}):
self.assertNotIsInstance(
self.validator.validate(value, **type_args), Issue)
def assertInvalid(self, value):
def assertInvalid(self, value, type_args={}):
self.assertIsInstance(
self.validator.validate(value), Issue)
self.validator.validate(value, **type_args), Issue)
class StringTypeValidatorTests(TypeValidatorTestHelper, unittest.TestCase):
@ -31,6 +32,19 @@ class StringTypeValidatorTests(TypeValidatorTestHelper, unittest.TestCase):
self.assertEqual(s, self.validator.validate(s))
class EnumTypeValidatorTests(TypeValidatorTestHelper, unittest.TestCase):
type_name = 'enum'
def test_listed_value(self):
self.assertValid('foo', type_args={'values': ['foo', 'bar']})
def test_unlisted_value(self):
self.assertInvalid('baz', type_args={'values': ['foo', 'bar']})
def test_with_no_values_returns_error(self):
self.assertInvalid('foo')
class BooleanTypeValidatorTests(TypeValidatorTestHelper, unittest.TestCase):
type_name = 'boolean'
@ -250,6 +264,16 @@ class HostAndPortTypeValidatorTests(TypeValidatorTestHelper,
self.assertInvalid('10.0.0.1:65536')
class RegexTypeValidatorTests(TypeValidatorTestHelper, unittest.TestCase):
type_name = 'regex'
def test_valid_regex(self):
self.assertValid('\d+\.\d+\.\d+\.\d+')
def test_invalid_regex(self):
self.assertInvalid('(\d+')
class StringListTypeValidatorTests(TypeValidatorTestHelper, unittest.TestCase):
type_name = 'string_list'