# 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