diff options
Diffstat (limited to 'azure/aria/aria-extension-cloudify/src/aria/aria/modeling/functions.py')
-rw-r--r-- | azure/aria/aria-extension-cloudify/src/aria/aria/modeling/functions.py | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/azure/aria/aria-extension-cloudify/src/aria/aria/modeling/functions.py b/azure/aria/aria-extension-cloudify/src/aria/aria/modeling/functions.py new file mode 100644 index 0000000..554bbfb --- /dev/null +++ b/azure/aria/aria-extension-cloudify/src/aria/aria/modeling/functions.py @@ -0,0 +1,140 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. + +""" +Mechanism for evaluating intrinsic functions. +""" +from ..parser.exceptions import InvalidValueError +from ..parser.consumption import ConsumptionContext +from ..utils.collections import OrderedDict +from ..utils.type import full_type_name +from . import exceptions + + +class Function(object): + """ + Base class for intrinsic functions. Serves as a placeholder for a value that should eventually + be derived by "evaluating" (calling) the function. + + Note that this base class is provided as a convenience and you do not have to inherit it: any + object with an ``__evaluate__`` method would be treated similarly. + """ + + @property + def as_raw(self): + raise NotImplementedError + + def __evaluate__(self, container_holder): + """ + Evaluates the function if possible. + + :rtype: :class:`Evaluation` (or any object with ``value`` and ``final`` properties) + :raises CannotEvaluateFunctionException: if cannot be evaluated at this time (do *not* just + return ``None``) + """ + + raise NotImplementedError + + def __deepcopy__(self, memo): + # Circumvent cloning in order to maintain our state + return self + + +class Evaluation(object): + """ + An evaluated :class:`Function` return value. + + :ivar value: evaluated value + :ivar final: whether the value is final + :vartype final: boolean + """ + + def __init__(self, value, final=False): + self.value = value + self.final = final + + +def evaluate(value, container_holder, report_issues=False): # pylint: disable=too-many-branches + """ + Recursively attempts to call ``__evaluate__``. If an evaluation occurred will return an + :class:`Evaluation`, otherwise it will be ``None``. If any evaluation is non-final, then the + entire evaluation will also be non-final. + + The ``container_holder`` argument should have three properties: ``container`` should return + the model that contains the value, ``service`` should return the containing + :class:`~aria.modeling.models.Service` model or None, and ``service_template`` should return the + containing :class:`~aria.modeling.models.ServiceTemplate` model or ``None``. + """ + + evaluated = False + final = True + + if hasattr(value, '__evaluate__'): + try: + evaluation = value.__evaluate__(container_holder) + + # Verify evaluation structure + if (evaluation is None) \ + or (not hasattr(evaluation, 'value')) \ + or (not hasattr(evaluation, 'final')): + raise InvalidValueError('bad __evaluate__ implementation: {0}' + .format(full_type_name(value))) + + evaluated = True + value = evaluation.value + final = evaluation.final + + # The evaluated value might itself be evaluable + evaluation = evaluate(value, container_holder, report_issues) + if evaluation is not None: + value = evaluation.value + if not evaluation.final: + final = False + except exceptions.CannotEvaluateFunctionException: + pass + except InvalidValueError as e: + if report_issues: + context = ConsumptionContext.get_thread_local() + context.validation.report(e.issue) + + elif isinstance(value, list): + evaluated_list = [] + for v in value: + evaluation = evaluate(v, container_holder, report_issues) + if evaluation is not None: + evaluated_list.append(evaluation.value) + evaluated = True + if not evaluation.final: + final = False + else: + evaluated_list.append(v) + if evaluated: + value = evaluated_list + + elif isinstance(value, dict): + evaluated_dict = OrderedDict() + for k, v in value.iteritems(): + evaluation = evaluate(v, container_holder, report_issues) + if evaluation is not None: + evaluated_dict[k] = evaluation.value + evaluated = True + if not evaluation.final: + final = False + else: + evaluated_dict[k] = v + if evaluated: + value = evaluated_dict + + return Evaluation(value, final) if evaluated else None |