summaryrefslogtreecommitdiffstats
path: root/azure/aria/aria-extension-cloudify/src/aria/aria/modeling/functions.py
diff options
context:
space:
mode:
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.py140
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