config: Add ConfigValue base class and tests.
This commit is contained in:
parent
ba425d8ccb
commit
c22f0f5f9d
@ -17,3 +17,56 @@ def validate_maximum(value, maximum):
|
||||
"""Maximum validation, normally called in config value's validate()."""
|
||||
if maximum is not None and value > maximum:
|
||||
raise ValueError('must be smaller than %s.' % maximum)
|
||||
|
||||
|
||||
class ConfigValue(object):
|
||||
"""Represents a config key's value and how to handle it.
|
||||
|
||||
Normally you will only be interacting with sub-classes for config values
|
||||
that encode either deserialization behavior and/or validation.
|
||||
|
||||
Each config value should be used for the following actions:
|
||||
|
||||
1. Deserializing from a raw string and validating, raising ValueError on
|
||||
failure.
|
||||
2. Serializing a value back to a string that can be stored in a config.
|
||||
3. Formatting a value to a printable form (useful for masking secrets).
|
||||
|
||||
:class:`None` values should not be deserialized, serialized or formatted,
|
||||
the code interacting with the config should simply skip None config values.
|
||||
"""
|
||||
|
||||
#: Collection of valid choices for converted value. Must be combined with
|
||||
#: :function:`validate_choices` in :method:`validate` do any thing.
|
||||
choices = None
|
||||
|
||||
#: Minimum of converted value. Must be combined with
|
||||
#: :function:`validate_minimum` in :method:`validate` do any thing.
|
||||
minimum = None
|
||||
|
||||
#: Maximum of converted value. Must be combined with
|
||||
#: :function:`validate_maximum` in :method:`validate` do any thing.
|
||||
maximum = None
|
||||
|
||||
#: Indicate if we should mask the when printing for human consumption.
|
||||
secret = None
|
||||
|
||||
def __init__(self, choices=None, minimum=None, maximum=None, secret=None):
|
||||
self.choices = choices
|
||||
self.minimum = minimum
|
||||
self.maximum = maximum
|
||||
self.secret = secret
|
||||
|
||||
def deserialize(self, value):
|
||||
"""Cast raw string to appropriate type."""
|
||||
return value
|
||||
|
||||
def serialize(self, value):
|
||||
"""Convert value back to string for saving."""
|
||||
return str(value)
|
||||
|
||||
def format(self, value):
|
||||
"""Format value for display."""
|
||||
if self.secret:
|
||||
return '********'
|
||||
return self.serialize(value)
|
||||
|
||||
@ -51,3 +51,38 @@ class ValidateMaximumTest(unittest.TestCase):
|
||||
def test_to_large_value_fails_with_zero_as_maximum(self):
|
||||
with self.assertRaises(ValueError):
|
||||
config.validate_maximum(5, 0)
|
||||
|
||||
|
||||
class ConfigValueTest(unittest.TestCase):
|
||||
def test_init(self):
|
||||
value = config.ConfigValue()
|
||||
self.assertIsNone(value.choices)
|
||||
self.assertIsNone(value.minimum)
|
||||
self.assertIsNone(value.maximum)
|
||||
self.assertIsNone(value.secret)
|
||||
|
||||
def test_init_with_params(self):
|
||||
value = config.ConfigValue(
|
||||
choices=['foo'], minimum=0, maximum=10, secret=True)
|
||||
self.assertEqual(['foo'], value.choices)
|
||||
self.assertEqual(0, value.minimum)
|
||||
self.assertEqual(10, value.maximum)
|
||||
self.assertEqual(True, value.secret)
|
||||
|
||||
def test_deserialize_passes_through(self):
|
||||
value = config.ConfigValue()
|
||||
obj = object()
|
||||
self.assertEqual(obj, value.deserialize(obj))
|
||||
|
||||
def test_serialize_converts_to_string(self):
|
||||
value = config.ConfigValue()
|
||||
self.assertIsInstance(value.serialize(object()), basestring)
|
||||
|
||||
def test_format_uses_serialize(self):
|
||||
value = config.ConfigValue()
|
||||
obj = object()
|
||||
self.assertEqual(value.serialize(obj), value.format(obj))
|
||||
|
||||
def test_format_masks_secrets(self):
|
||||
value = config.ConfigValue(secret=True)
|
||||
self.assertEqual('********', value.format(object()))
|
||||
|
||||
Loading…
Reference in New Issue
Block a user