From a20d03fb0f5ef437eef0ff61dec5a2b3eaaaf685 Mon Sep 17 00:00:00 2001 From: Ekaterina Chernova Date: Thu, 3 Dec 2015 16:13:02 +0300 Subject: [PATCH] Add OneOf smart type It is a new smart type, that verifies function parameter whether it belongs to the on of the specified in the function declaration classes. Targets blueprint mock-context-manager Change-Id: I9eccdf3af1802c731bfdfb93df92c4ad902a4d34 --- murano/dsl/dsl.py | 54 ++++++++++++++++++ .../tests/unit/dsl/meta/OneOfSmartType.yaml | 39 +++++++++++++ murano/tests/unit/dsl/test_dsl.py | 56 +++++++++++++++++++ 3 files changed, 149 insertions(+) create mode 100644 murano/tests/unit/dsl/meta/OneOfSmartType.yaml create mode 100644 murano/tests/unit/dsl/test_dsl.py diff --git a/murano/dsl/dsl.py b/murano/dsl/dsl.py index f7a7c26f..9ceacf73 100644 --- a/murano/dsl/dsl.py +++ b/murano/dsl/dsl.py @@ -15,6 +15,7 @@ import inspect import os.path +from yaql.language import exceptions as yaql_exc from yaql.language import expressions as yaql_expressions from yaql.language import utils from yaql.language import yaqltypes @@ -324,3 +325,56 @@ def to_mutable(obj, yaql_engine): limiter = lambda it: utils.limit_iterable(it, constants.ITERATORS_LIMIT) return converter(obj, limiter, yaql_engine, converter) + + +class OneOf(yaqltypes.SmartType): + def __init__(self, *args, **kwargs): + self.nullable = kwargs.pop('nullable', False) + super(OneOf, self).__init__(self.nullable) + + self.choices = [] + for item in args: + if isinstance(item, type): + item = yaqltypes.PythonType(item) + self.choices.append(item) + + def _check_match(self, value, context, engine, *args, **kwargs): + for type_to_check in self.choices: + check_result = type_to_check.check(value, context, engine, + *args, **kwargs) + if check_result: + return type_to_check + + def check(self, value, context, engine, *args, **kwargs): + if isinstance(value, yaql_expressions.Constant): + if value.value is None: + value = None + if value is None: + return self.nullable + + check_result = self._check_match(value, context, engine, + *args, **kwargs) + if check_result: + return True + return False + + def convert(self, value, receiver, context, function_spec, engine, + *args, **kwargs): + if isinstance(value, yaql_expressions.Constant): + if value.value is None: + value = None + if value is None: + if self.nullable: + return None + else: + suitable_type = False + + else: + suitable_type = self._check_match(value, context, engine, + *args, **kwargs) + if suitable_type: + converted_value = suitable_type.convert(value, receiver, context, + function_spec, engine, + *args, **kwargs) + return converted_value + raise yaql_exc.ArgumentValueException() diff --git a/murano/tests/unit/dsl/meta/OneOfSmartType.yaml b/murano/tests/unit/dsl/meta/OneOfSmartType.yaml new file mode 100644 index 00000000..f063f59a --- /dev/null +++ b/murano/tests/unit/dsl/meta/OneOfSmartType.yaml @@ -0,0 +1,39 @@ +Name: OneOfSmartType + +Namespaces: + sys: io.murano.system + +Methods: + testNegative1: + Body: + - testOneOf(1, 'test') + + testNegative2: + Body: + - testOneOf('test', 1) + + testNegative3: + Body: + - testOneOf() + + testNegative4: + Body: + - testOneOf(null, null) + + testNullable: + Body: + - trace(testOneOf('io.murano.system.AgentListener', null)) + + testPositive1: + Body: + - trace(testOneOf(sys:AgentListener, 2)) + + testPositive2: + Body: + - $agentListener: new(sys:AgentListener) + - trace(testOneOf($agentListener, 2)) + + testPositive3: + Body: + - $agentListener: new(sys:AgentListener) + - trace(testOneOf($agentListener, true)) \ No newline at end of file diff --git a/murano/tests/unit/dsl/test_dsl.py b/murano/tests/unit/dsl/test_dsl.py new file mode 100644 index 00000000..291584ed --- /dev/null +++ b/murano/tests/unit/dsl/test_dsl.py @@ -0,0 +1,56 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from yaql.language import exceptions as yaql_exc +from yaql.language import specs +from yaql.language import yaqltypes + +from murano.dsl import dsl +from murano.dsl import dsl_types +from murano.dsl import exceptions as dsl_exc +from murano.tests.unit.dsl.foundation import object_model as om +from murano.tests.unit.dsl.foundation import test_case + + +@specs.parameter('param1', dsl.OneOf(dsl.MuranoTypeName(), + dsl_types.MuranoObject)) +@specs.parameter('param2', dsl.OneOf(yaqltypes.NumericConstant(), + yaqltypes.BooleanConstant(), + nullable=True)) +def test_one_of(param1, param2): + return 'Passed' + + +class TestOneOf(test_case.DslTestCase): + def setUp(self): + super(TestOneOf, self).setUp() + + self.register_function(test_one_of, 'testOneOf') + self.runner = self.new_runner(om.Object('OneOfSmartType')) + + def test_negative(self): + self.assertRaises(yaql_exc.NoMatchingFunctionException, + self.runner.testNegative1) + self.assertRaises(dsl_exc.NoClassFound, self.runner.testNegative2) + self.assertRaises(yaql_exc.NoMatchingFunctionException, + self.runner.testNegative3) + + def test_nullable(self): + self.runner.testNullable() + self.assertEqual(['Passed'], self.traces) + + def test_positive(self): + self.runner.testPositive1() + self.runner.testPositive2() + self.runner.testPositive3() + + self.assertEqual(['Passed', 'Passed', 'Passed'], self.traces)