From 15bdfe029eb9274fd37ad2d6e8ec3001ec6eaf8e Mon Sep 17 00:00:00 2001 From: Steve Baker Date: Wed, 3 Jul 2013 15:21:23 +1200 Subject: [PATCH] Define behaviour for properties with None values. Now that Ref and Fn::GetAtt might return None values, behaviour for properties with None values needs definition. This change will treat a None value in the following way: - if there is a default in the schema, that value will be used - otherwise a default is used depending on the type ('', False, 0, [] {}) Fixes bug: #1192142 Change-Id: I59ae32c1c32b1bfede7d775aa845cd14246040e1 --- heat/engine/properties.py | 21 +++++- heat/tests/test_properties.py | 136 ++++++++++++++++++++++++++++++++++ 2 files changed, 154 insertions(+), 3 deletions(-) diff --git a/heat/engine/properties.py b/heat/engine/properties.py index 462752d9..e71a54da 100644 --- a/heat/engine/properties.py +++ b/heat/engine/properties.py @@ -79,7 +79,16 @@ class Property(object): except ValueError: return float(value) + def _validate_integer(self, value): + if value is None: + value = self.has_default() and self.default() or 0 + if not isinstance(value, int): + raise TypeError('value is not an integer') + return self._validate_number(value) + def _validate_number(self, value): + if value is None: + value = self.has_default() and self.default() or 0 self._check_allowed(value) num = self.str_to_num(value) @@ -93,6 +102,8 @@ class Property(object): return value def _validate_string(self, value): + if value is None: + value = self.has_default() and self.default() or '' if not isinstance(value, basestring): raise ValueError('Value must be a string') @@ -119,6 +130,8 @@ class Property(object): return value def _validate_map(self, value): + if value is None: + value = self.has_default() and self.default() or {} if not isinstance(value, collections.Mapping): raise TypeError('"%s" is not a map' % value) @@ -131,6 +144,8 @@ class Property(object): return children def _validate_list(self, value): + if value is None: + value = self.has_default() and self.default() or [] if (not isinstance(value, collections.Sequence) or isinstance(value, basestring)): raise TypeError('"%s" is not a list' % repr(value)) @@ -147,6 +162,8 @@ class Property(object): return children def _validate_bool(self, value): + if value is None: + value = self.has_default() and self.default() or False if isinstance(value, bool): return value normalised = value.lower() @@ -160,9 +177,7 @@ class Property(object): if t == STRING: return self._validate_string(value) elif t == INTEGER: - if not isinstance(value, int): - raise TypeError('value is not an integer') - return self._validate_number(value) + return self._validate_integer(value) elif t == NUMBER: return self._validate_number(value) elif t == MAP: diff --git a/heat/tests/test_properties.py b/heat/tests/test_properties.py index 0d537b1a..4bdf9f46 100644 --- a/heat/tests/test_properties.py +++ b/heat/tests/test_properties.py @@ -332,6 +332,82 @@ class PropertiesTest(testtools.TestCase): def test_bad_key(self): self.assertEqual(self.props.get('foo', 'wibble'), 'wibble') + def test_none_string(self): + schema = {'foo': {'Type': 'String'}} + props = properties.Properties(schema, {'foo': None}) + self.assertEqual('', props['foo']) + + def test_none_integer(self): + schema = {'foo': {'Type': 'Integer'}} + props = properties.Properties(schema, {'foo': None}) + self.assertEqual(0, props['foo']) + + def test_none_number(self): + schema = {'foo': {'Type': 'Number'}} + props = properties.Properties(schema, {'foo': None}) + self.assertEqual(0, props['foo']) + + def test_none_boolean(self): + schema = {'foo': {'Type': 'Boolean'}} + props = properties.Properties(schema, {'foo': None}) + self.assertEqual(False, props['foo']) + + def test_none_map(self): + schema = {'foo': {'Type': 'Map'}} + props = properties.Properties(schema, {'foo': None}) + self.assertEqual({}, props['foo']) + + def test_none_list(self): + schema = {'foo': {'Type': 'List'}} + props = properties.Properties(schema, {'foo': None}) + self.assertEqual([], props['foo']) + + def test_none_default_string(self): + schema = {'foo': {'Type': 'String', 'Default': 'bar'}} + props = properties.Properties(schema, {'foo': None}) + self.assertEqual('bar', props['foo']) + + def test_none_default_integer(self): + schema = {'foo': {'Type': 'Integer', 'Default': 42}} + props = properties.Properties(schema, {'foo': None}) + self.assertEqual(42, props['foo']) + + schema = {'foo': {'Type': 'Integer', 'Default': 0}} + props = properties.Properties(schema, {'foo': None}) + self.assertEqual(0, props['foo']) + + schema = {'foo': {'Type': 'Integer', 'Default': -273}} + props = properties.Properties(schema, {'foo': None}) + self.assertEqual(-273, props['foo']) + + def test_none_default_number(self): + schema = {'foo': {'Type': 'Number', 'Default': 42.0}} + props = properties.Properties(schema, {'foo': None}) + self.assertEqual(42.0, props['foo']) + + schema = {'foo': {'Type': 'Number', 'Default': 0.0}} + props = properties.Properties(schema, {'foo': None}) + self.assertEqual(0.0, props['foo']) + + schema = {'foo': {'Type': 'Number', 'Default': -273.15}} + props = properties.Properties(schema, {'foo': None}) + self.assertEqual(-273.15, props['foo']) + + def test_none_default_boolean(self): + schema = {'foo': {'Type': 'Boolean', 'Default': True}} + props = properties.Properties(schema, {'foo': None}) + self.assertEqual(True, props['foo']) + + def test_none_default_map(self): + schema = {'foo': {'Type': 'Map', 'Default': {'bar': 'baz'}}} + props = properties.Properties(schema, {'foo': None}) + self.assertEqual({'bar': 'baz'}, props['foo']) + + def test_none_default_list(self): + schema = {'foo': {'Type': 'List', 'Default': ['one', 'two']}} + props = properties.Properties(schema, {'foo': None}) + self.assertEqual(['one', 'two'], props['foo']) + class PropertiesValidationTest(testtools.TestCase): def test_required(self): @@ -368,3 +444,63 @@ class PropertiesValidationTest(testtools.TestCase): schema = {'foo': {'Type': 'String'}} props = properties.Properties(schema, {'food': 42}) self.assertRaises(exception.StackValidationFailed, props.validate) + + def test_none_string(self): + schema = {'foo': {'Type': 'String'}} + props = properties.Properties(schema, {'foo': None}) + self.assertEqual(props.validate(), None) + + def test_none_integer(self): + schema = {'foo': {'Type': 'Integer'}} + props = properties.Properties(schema, {'foo': None}) + self.assertEqual(props.validate(), None) + + def test_none_number(self): + schema = {'foo': {'Type': 'Number'}} + props = properties.Properties(schema, {'foo': None}) + self.assertEqual(props.validate(), None) + + def test_none_boolean(self): + schema = {'foo': {'Type': 'Boolean'}} + props = properties.Properties(schema, {'foo': None}) + self.assertEqual(props.validate(), None) + + def test_none_map(self): + schema = {'foo': {'Type': 'Map'}} + props = properties.Properties(schema, {'foo': None}) + self.assertEqual(props.validate(), None) + + def test_none_list(self): + schema = {'foo': {'Type': 'List'}} + props = properties.Properties(schema, {'foo': None}) + self.assertEqual(props.validate(), None) + + def test_none_default_string(self): + schema = {'foo': {'Type': 'String', 'Default': 'bar'}} + props = properties.Properties(schema, {'foo': None}) + self.assertEqual(props.validate(), None) + + def test_none_default_integer(self): + schema = {'foo': {'Type': 'Integer', 'Default': 42}} + props = properties.Properties(schema, {'foo': None}) + self.assertEqual(props.validate(), None) + + def test_none_default_number(self): + schema = {'foo': {'Type': 'Number', 'Default': 42.0}} + props = properties.Properties(schema, {'foo': None}) + self.assertEqual(props.validate(), None) + + def test_none_default_boolean(self): + schema = {'foo': {'Type': 'Boolean', 'Default': True}} + props = properties.Properties(schema, {'foo': None}) + self.assertEqual(props.validate(), None) + + def test_none_default_map(self): + schema = {'foo': {'Type': 'Map', 'Default': {'bar': 'baz'}}} + props = properties.Properties(schema, {'foo': None}) + self.assertEqual(props.validate(), None) + + def test_none_default_list(self): + schema = {'foo': {'Type': 'List', 'Default': ['one', 'two']}} + props = properties.Properties(schema, {'foo': None}) + self.assertEqual(props.validate(), None) -- 2.45.2