From eb4d4a5b65c7994d76a72607f6c5faf0a0ffa888 Mon Sep 17 00:00:00 2001 From: Octavian Ciuhandu Date: Fri, 5 Aug 2016 19:04:16 +0300 Subject: [PATCH] Adds new fields and field types Adds IPV4AndV6Address, NonNegativeFloat, NonNegativeInteger field types and their respective fields, which can be used later on in different projects. Change-Id: I58369998b7fab32c391813c06e9000867aa7887b --- oslo_versionedobjects/fields.py | 45 +++++++++++++++++ oslo_versionedobjects/tests/test_fields.py | 56 ++++++++++++++++++++++ 2 files changed, 101 insertions(+) diff --git a/oslo_versionedobjects/fields.py b/oslo_versionedobjects/fields.py index 2ba54d95..9182f4e1 100644 --- a/oslo_versionedobjects/fields.py +++ b/oslo_versionedobjects/fields.py @@ -385,6 +385,15 @@ class Integer(FieldType): return {'type': ['integer']} +class NonNegativeInteger(FieldType): + @staticmethod + def coerce(obj, attr, value): + v = int(value) + if v < 0: + raise ValueError(_('Value must be >= 0 for field %s') % attr) + return v + + class Float(FieldType): def coerce(self, obj, attr, value): return float(value) @@ -393,6 +402,15 @@ class Float(FieldType): return {'type': ['number']} +class NonNegativeFloat(FieldType): + @staticmethod + def coerce(obj, attr, value): + v = float(value) + if v < 0: + raise ValueError(_('Value must be >= 0 for field %s') % attr) + return v + + class Boolean(FieldType): @staticmethod def coerce(obj, attr, value): @@ -482,6 +500,17 @@ class IPV6Address(IPAddress): return result +class IPV4AndV6Address(IPAddress): + @staticmethod + def coerce(obj, attr, value): + result = IPAddress.coerce(obj, attr, value) + if result.version != 4 and result.version != 6: + raise ValueError(_('Network "%(val)s" is not valid ' + 'in field %(attr)s') % + {'val': value, 'attr': attr}) + return result + + class IPNetwork(IPAddress): @staticmethod def coerce(obj, attr, value): @@ -898,10 +927,18 @@ class IntegerField(AutoTypedField): AUTO_TYPE = Integer() +class NonNegativeIntegerField(AutoTypedField): + AUTO_TYPE = NonNegativeInteger() + + class FloatField(AutoTypedField): AUTO_TYPE = Float() +class NonNegativeFloatField(AutoTypedField): + AUTO_TYPE = NonNegativeFloat() + + # This is a strict interpretation of boolean # values using Python's semantics for truth/falsehood class BooleanField(AutoTypedField): @@ -968,6 +1005,10 @@ class ListOfSetsOfIntegersField(AutoTypedField): AUTO_TYPE = List(Set(Integer())) +class ListOfIntegersField(AutoTypedField): + AUTO_TYPE = List(Integer()) + + class ListOfDictOfNullableStringsField(AutoTypedField): AUTO_TYPE = List(Dict(String(), nullable=True)) @@ -998,6 +1039,10 @@ class IPV6AddressField(AutoTypedField): AUTO_TYPE = IPV6Address() +class IPV4AndV6AddressField(AutoTypedField): + AUTO_TYPE = IPV4AndV6Address() + + class IPNetworkField(AutoTypedField): AUTO_TYPE = IPNetwork() diff --git a/oslo_versionedobjects/tests/test_fields.py b/oslo_versionedobjects/tests/test_fields.py index e9eed260..0ecd5c72 100644 --- a/oslo_versionedobjects/tests/test_fields.py +++ b/oslo_versionedobjects/tests/test_fields.py @@ -444,6 +444,16 @@ class TestInteger(TestField): self.field.get_schema()) +class TestNonNegativeInteger(TestField): + def setUp(self): + super(TestNonNegativeInteger, self).setUp() + self.field = fields.NonNegativeIntegerField() + self.coerce_good_values = [(1, 1), ('1', 1)] + self.coerce_bad_values = ['-2', '4.2', 'foo', None] + self.to_primitive_values = self.coerce_good_values[0:1] + self.from_primitive_values = self.coerce_good_values[0:1] + + class TestFloat(TestField): def setUp(self): super(TestFloat, self).setUp() @@ -461,6 +471,16 @@ class TestFloat(TestField): self.field.get_schema()) +class TestNonNegativeFloat(TestField): + def setUp(self): + super(TestNonNegativeFloat, self).setUp() + self.field = fields.NonNegativeFloatField() + self.coerce_good_values = [(1.1, 1.1), ('1.1', 1.1)] + self.coerce_bad_values = ['-4.2', 'foo', None] + self.to_primitive_values = self.coerce_good_values[0:1] + self.from_primitive_values = self.coerce_good_values[0:1] + + class TestBoolean(TestField): def setUp(self): super(TestField, self).setUp() @@ -739,6 +759,20 @@ class TestListOfSetsOfIntegers(TestField): self.assertEqual('[set([1,2])]', self.field.stringify([set([1, 2])])) +class TestListOfIntegers(TestField): + def setUp(self): + super(TestListOfIntegers, self).setUp() + self.field = fields.ListOfIntegersField() + self.coerce_good_values = [(['1', 2], [1, 2]), + ([1, 2], [1, 2])] + self.coerce_bad_values = [['foo']] + self.to_primitive_values = [([1], [1])] + self.from_primitive_values = [([1], [1])] + + def test_stringify(self): + self.assertEqual('[[1, 2]]', self.field.stringify([[1, 2]])) + + class TestLocalMethods(test.TestCase): @mock.patch.object(obj_base.LOG, 'exception') def test__make_class_properties_setter_value_error(self, mock_log): @@ -971,6 +1005,28 @@ class TestIPAddressV6(TestField): netaddr.IPAddress('::1'))] +class TestIPV4AndV6Address(TestField): + def setUp(self): + super(TestIPV4AndV6Address, self).setUp() + self.field = fields.IPV4AndV6Address() + self.coerce_good_values = [('::1', netaddr.IPAddress('::1')), + (netaddr.IPAddress('::1'), + netaddr.IPAddress('::1')), + ('1.2.3.4', + netaddr.IPAddress('1.2.3.4')), + (netaddr.IPAddress('1.2.3.4'), + netaddr.IPAddress('1.2.3.4'))] + self.coerce_bad_values = ['1-2', 'foo'] + self.to_primitive_values = [(netaddr.IPAddress('::1'), + '::1'), + (netaddr.IPAddress('1.2.3.4'), + '1.2.3.4')] + self.from_primitive_values = [('::1', + netaddr.IPAddress('::1')), + ('1.2.3.4', + netaddr.IPAddress('1.2.3.4'))] + + class TestIPNetwork(TestField): def setUp(self): super(TestIPNetwork, self).setUp()