Add regex validation to String type
This lets you specify a regex that string options must match (unanchored). Change-Id: I0be613ee38a4752b135de5ee7f189621670d1071
This commit is contained in:
parent
9a1ba80ce2
commit
3f7b642c60
oslo_config
@ -12,7 +12,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
import re
|
||||
import unittest
|
||||
|
||||
from oslo_config import types
|
||||
@ -98,6 +98,34 @@ class StringTypeTests(TypeTestHelper, unittest.TestCase):
|
||||
def test_not_equal_to_other_class(self):
|
||||
self.assertFalse(types.String() == types.Integer())
|
||||
|
||||
def test_regex_matches(self):
|
||||
self.type_instance = types.String(regex=re.compile("^[A-Z]"))
|
||||
self.assertConvertedValue("Foo", "Foo")
|
||||
|
||||
def test_regex_matches_uncompiled(self):
|
||||
self.type_instance = types.String(regex="^[A-Z]")
|
||||
self.assertConvertedValue("Foo", "Foo")
|
||||
|
||||
def test_regex_fails(self):
|
||||
self.type_instance = types.String(regex=re.compile("^[A-Z]"))
|
||||
self.assertInvalid("foo")
|
||||
|
||||
def test_regex_and_choices_raises(self):
|
||||
self.assertRaises(ValueError,
|
||||
types.String,
|
||||
regex=re.compile("^[A-Z]"),
|
||||
choices=["Foo", "Bar", "baz"])
|
||||
|
||||
def test_equal_with_same_regex(self):
|
||||
t1 = types.String(regex=re.compile("^[A-Z]"))
|
||||
t2 = types.String(regex=re.compile("^[A-Z]"))
|
||||
self.assertTrue(t1 == t2)
|
||||
|
||||
def test_not_equal_with_different_regex(self):
|
||||
t1 = types.String(regex=re.compile("^[A-Z]"))
|
||||
t2 = types.String(regex=re.compile("^[a-z]"))
|
||||
self.assertFalse(t1 == t2)
|
||||
|
||||
|
||||
class BooleanTypeTests(TypeTestHelper, unittest.TestCase):
|
||||
type = types.Boolean()
|
||||
|
@ -18,6 +18,8 @@ Use these classes as values for the `type` argument to
|
||||
:class:`oslo_config.cfg.Opt` and its subclasses.
|
||||
|
||||
"""
|
||||
import re
|
||||
|
||||
import netaddr
|
||||
import six
|
||||
|
||||
@ -36,20 +38,28 @@ class String(ConfigType):
|
||||
|
||||
String values do not get transformed and are returned as str objects.
|
||||
|
||||
:param choices: Optional sequence of valid values.
|
||||
:param choices: Optional sequence of valid values. Mutually
|
||||
exclusive with 'regex'.
|
||||
:param quotes: If True and string is enclosed with single or double
|
||||
quotes, will strip those quotes. Will signal error if
|
||||
string have quote at the beginning and no quote at
|
||||
the end. Turned off by default. Useful if used with
|
||||
container types like List.
|
||||
:param regex: Optional regular expression (string or compiled
|
||||
regex) that the value must match on an unanchored
|
||||
search. Mutually exclusive with 'choices'.
|
||||
"""
|
||||
|
||||
BASE_TYPES = six.string_types
|
||||
|
||||
def __init__(self, choices=None, quotes=False):
|
||||
def __init__(self, choices=None, quotes=False, regex=None):
|
||||
super(String, self).__init__()
|
||||
if choices and regex:
|
||||
raise ValueError("'choices' and 'regex' cannot both be specified")
|
||||
|
||||
self.choices = choices
|
||||
self.quotes = quotes
|
||||
self.regex = re.compile(regex) if regex is not None else None
|
||||
|
||||
def __call__(self, value):
|
||||
value = str(value)
|
||||
@ -59,6 +69,10 @@ class String(ConfigType):
|
||||
raise ValueError('Non-closed quote: %s' % value)
|
||||
value = value[1:-1]
|
||||
|
||||
if self.regex and not self.regex.search(value):
|
||||
raise ValueError("Value %r doesn't match regex %r" %
|
||||
(value, self.regex.pattern))
|
||||
|
||||
if self.choices is None or value in self.choices:
|
||||
return value
|
||||
|
||||
@ -68,15 +82,21 @@ class String(ConfigType):
|
||||
repr(value)))
|
||||
|
||||
def __repr__(self):
|
||||
details = []
|
||||
if self.choices:
|
||||
return 'String(choices=%s)' % repr(self.choices)
|
||||
details.append("choices=%r" % self.choices)
|
||||
if self.regex:
|
||||
details.append("regex=%r" % self.regex.pattern)
|
||||
if details:
|
||||
return "String(%s)" % ",".join(details)
|
||||
return 'String'
|
||||
|
||||
def __eq__(self, other):
|
||||
return (
|
||||
(self.__class__ == other.__class__) and
|
||||
(self.choices == other.choices) and
|
||||
(self.quotes == other.quotes)
|
||||
(self.quotes == other.quotes) and
|
||||
(self.regex == other.regex)
|
||||
)
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user