diff --git a/heat/engine/function.py b/heat/engine/function.py index 2f48ff2e82..f5a091ab83 100644 --- a/heat/engine/function.py +++ b/heat/engine/function.py @@ -36,6 +36,15 @@ class Function(object): self.fn_name = fn_name self.args = args + def validate(self): + """ + Validate arguments without resolving the function. + + Function subclasses must override this method to validate their + args. + """ + validate(self.args) + @abc.abstractmethod def result(self): """ @@ -110,3 +119,15 @@ def resolve(snippet): return [resolve(v) for v in snippet] return snippet + + +def validate(snippet): + if isinstance(snippet, Function): + snippet.validate() + elif isinstance(snippet, collections.Mapping): + for v in snippet.values(): + validate(v) + elif (not isinstance(snippet, basestring) and + isinstance(snippet, collections.Iterable)): + for v in snippet: + validate(v) diff --git a/heat/tests/test_function.py b/heat/tests/test_function.py index e0d2241b3c..885846dce3 100644 --- a/heat/tests/test_function.py +++ b/heat/tests/test_function.py @@ -19,6 +19,10 @@ from heat.engine import function class TestFunction(function.Function): + def validate(self): + if len(self.args) < 2: + raise Exception(_('Need more arguments')) + def result(self): return 'wibble' @@ -80,3 +84,42 @@ class ResolveTest(HeatTestCase): self.assertEqual(['foo', {'bar': ['baz', {'blarg': 'wibble'}]}], result) self.assertIsNot(result, snippet) + + +class ValidateTest(HeatTestCase): + def setUp(self): + super(ValidateTest, self).setUp() + self.func = TestFunction(None, 'foo', ['bar', 'baz']) + + def test_validate_func(self): + self.assertIsNone(function.validate(self.func)) + self.func = TestFunction(None, 'foo', ['bar']) + ex = self.assertRaises(Exception, function.validate, self.func) + self.assertEqual('Need more arguments', str(ex)) + + def test_validate_dict(self): + snippet = {'foo': 'bar', 'blarg': self.func} + function.validate(snippet) + + self.func = TestFunction(None, 'foo', ['bar']) + snippet = {'foo': 'bar', 'blarg': self.func} + ex = self.assertRaises(Exception, function.validate, snippet) + self.assertEqual('Need more arguments', str(ex)) + + def test_validate_list(self): + snippet = ['foo', 'bar', 'baz', 'blarg', self.func] + function.validate(snippet) + + self.func = TestFunction(None, 'foo', ['bar']) + snippet = {'foo': 'bar', 'blarg': self.func} + ex = self.assertRaises(Exception, function.validate, snippet) + self.assertEqual('Need more arguments', str(ex)) + + def test_validate_all(self): + snippet = ['foo', {'bar': ['baz', {'blarg': self.func}]}] + function.validate(snippet) + + self.func = TestFunction(None, 'foo', ['bar']) + snippet = {'foo': 'bar', 'blarg': self.func} + ex = self.assertRaises(Exception, function.validate, snippet) + self.assertEqual('Need more arguments', str(ex))