summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTrevor McCasland <TM2086@att.com>2016-08-09 16:17:05 -0500
committerAlexis Lee <lxsli@hpe.com>2016-08-22 17:39:19 +0100
commita3c590e806b49cd15a9556dbc71f059e2c2d3962 (patch)
treef44f761e7deaa6d62c7b9a66e7e66882bbeb88a0
parent2449197425ea5a712d93191042414c540f2656e4 (diff)
downloadoslo-config-a3c590e806b49cd15a9556dbc71f059e2c2d3962.tar.gz
Add Range type
Add a type which accepts "1-3" to produce a range. This can be used to EG find a free port in a range or validate that a user input is acceptable. It should be possible to make a ListOpt(Range) which will accept "1-3,5-6". Change-Id: I9a2727522f25535a25c53ac89cd5e0096c87fef9 Co-Authored-By: Alexis Lee <lxsli@hpe.com>
-rw-r--r--oslo_config/tests/test_types.py40
-rw-r--r--oslo_config/types.py57
2 files changed, 97 insertions, 0 deletions
diff --git a/oslo_config/tests/test_types.py b/oslo_config/tests/test_types.py
index b53817d..524aa94 100644
--- a/oslo_config/tests/test_types.py
+++ b/oslo_config/tests/test_types.py
@@ -16,6 +16,7 @@ import re
import unittest
from oslo_config import types
+from six.moves import range as compat_range
class ConfigTypeTests(unittest.TestCase):
@@ -545,6 +546,45 @@ class ListTypeTests(TypeTestHelper, unittest.TestCase):
self.assertFalse(types.List() == types.Integer())
+class RangeTypeTests(TypeTestHelper, unittest.TestCase):
+ type = types.Range()
+
+ def assertRange(self, s, r1, r2, step=1):
+ self.assertEqual(list(compat_range(r1, r2, step)),
+ list(self.type_instance(s)))
+
+ def test_range(self):
+ self.assertRange('0-2', 0, 3)
+ self.assertRange('-2-0', -2, 1)
+ self.assertRange('2-0', 2, -1, -1)
+ self.assertRange('-3--1', -3, 0)
+ self.assertRange('-1--3', -1, -4, -1)
+ self.assertRange('-1', -1, 0)
+ self.assertInvalid('--1')
+ self.assertInvalid('4-')
+ self.assertInvalid('--')
+ self.assertInvalid('1.1-1.2')
+ self.assertInvalid('a-b')
+
+ def test_range_bounds(self):
+ self.type_instance = types.Range(1, 3)
+ self.assertRange('1-3', 1, 4)
+ self.assertRange('2-2', 2, 3)
+ self.assertRange('2', 2, 3)
+ self.assertInvalid('1-4')
+ self.assertInvalid('0-3')
+ self.assertInvalid('0-4')
+
+ def test_range_exclusive(self):
+ self.type_instance = types.Range(inclusive=False)
+ self.assertRange('0-2', 0, 2)
+ self.assertRange('-2-0', -2, 0)
+ self.assertRange('2-0', 2, 0, -1)
+ self.assertRange('-3--1', -3, -1)
+ self.assertRange('-1--3', -1, -3, -1)
+ self.assertRange('-1', -1, -1)
+
+
class DictTypeTests(TypeTestHelper, unittest.TestCase):
type = types.Dict()
diff --git a/oslo_config/types.py b/oslo_config/types.py
index 892b589..c28f335 100644
--- a/oslo_config/types.py
+++ b/oslo_config/types.py
@@ -27,6 +27,7 @@ import abc
import netaddr
import rfc3986
import six
+from six.moves import range as compat_range
@six.add_metaclass(abc.ABCMeta)
@@ -471,6 +472,62 @@ class List(ConfigType):
return ','.join(value)
+class Range(ConfigType):
+
+ """Range type.
+
+ Represents a range of integers. A range is identified by an integer both
+ sides of a '-' character. Negatives are allowed. A single number is also a
+ valid range.
+
+ :param min: Optional check that lower bound is greater than or equal to
+ min.
+ :param max: Optional check that upper bound is less than or equal to max.
+ :param inclusive: True if the right bound is to be included in the range.
+ :param type_name: Type name to be used in the sample config file.
+
+ .. versionadded:: 3.16
+ """
+
+ def __init__(self, min=None, max=None, inclusive=True,
+ type_name='range value'):
+ super(Range, self).__init__(type_name)
+ self.min = min
+ self.max = max
+ self.inclusive = inclusive
+
+ def __call__(self, value):
+ value = str(value)
+ num = "0|-?[1-9][0-9]*"
+ m = re.match("^(%s)(?:-(%s))?$" % (num, num), value)
+ if not m:
+ raise ValueError('Invalid Range: %s' % value)
+ left = int(m.group(1))
+ right = int(left if m.group(2) is None else m.group(2))
+
+ if left < right:
+ left = Integer(min=self.min)(left)
+ right = Integer(max=self.max)(right)
+ step = 1
+ else:
+ left = Integer(max=self.max)(left)
+ right = Integer(min=self.min)(right)
+ step = -1
+ if self.inclusive:
+ right += step
+ return compat_range(left, right, step)
+
+ def __eq__(self, other):
+ return (
+ (self.__class__ == other.__class__) and
+ (self.min == other.min) and
+ (self.max == other.max)
+ )
+
+ def _formatter(self, value):
+ return value
+
+
class Dict(ConfigType):
"""Dictionary type.