| # 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.consumption import ConsumptionContext |
| from ..parser.exceptions import InvalidValueError |
| from ..utils.collections import OrderedDict |
| 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') |
| |
| 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 |