Repository: incubator-ariatosca
Updated Branches:
  refs/heads/ARIA-139-attributes d9cc24dce -> 6f349dd72


Progress on new function mechanism

Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo
Commit: 
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/6f349dd7
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/6f349dd7
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/6f349dd7

Branch: refs/heads/ARIA-139-attributes
Commit: 6f349dd723c82b9a889edbe3bb5a7e9d0b137c62
Parents: d9cc24d
Author: Tal Liron <[email protected]>
Authored: Wed Apr 26 21:16:55 2017 -0500
Committer: Tal Liron <[email protected]>
Committed: Wed Apr 26 21:16:55 2017 -0500

----------------------------------------------------------------------
 aria/core.py                                    |   6 +-
 aria/modeling/functions.py                      |  99 ++++++++++-
 aria/modeling/mixins.py                         |   2 +-
 aria/modeling/service_common.py                 | 178 +++++++++++++++++--
 aria/modeling/service_instance.py               |  72 ++++----
 aria/modeling/service_template.py               |  80 ++++-----
 aria/modeling/utils.py                          |  45 +----
 aria/parser/consumption/__init__.py             |   9 +-
 aria/parser/consumption/modeling.py             |   4 +-
 .../simple_v1_0/functions.py                    | 103 ++++++-----
 .../simple_v1_0/modeling/__init__.py            |   4 +-
 .../simple_v1_0/modeling/data_types.py          |   2 +
 12 files changed, 417 insertions(+), 187 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/6f349dd7/aria/core.py
----------------------------------------------------------------------
diff --git a/aria/core.py b/aria/core.py
index af1984a..cc943ef 100644
--- a/aria/core.py
+++ b/aria/core.py
@@ -77,10 +77,14 @@ class Core(object):
             consumption.ConsumerChain(
                 context,
                 (
+                    consumption.CoerceServiceInstanceValues,
+                    consumption.ValidateServiceInstance,
                     consumption.SatisfyRequirements,
+                    consumption.CoerceServiceInstanceValues,
                     consumption.ValidateCapabilities,
                     consumption.FindHosts,
-                    consumption.ConfigureOperations
+                    consumption.ConfigureOperations,
+                    consumption.CoerceServiceInstanceValues
                 )).consume()
             if context.validation.dump_issues():
                 raise exceptions.InstantiationError('Failed to instantiate 
service template')

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/6f349dd7/aria/modeling/functions.py
----------------------------------------------------------------------
diff --git a/aria/modeling/functions.py b/aria/modeling/functions.py
index 02f4454..f0d6c81 100644
--- a/aria/modeling/functions.py
+++ b/aria/modeling/functions.py
@@ -13,20 +13,113 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+from ..parser.consumption import ConsumptionContext
+from ..parser.exceptions import InvalidValueError
+from ..utils.collections import OrderedDict
+from . import exceptions
+
+
 class Function(object):
     """
-    An intrinsic function.
+    Base class for intrinsic functions. Serves as a placeholder for a value 
that should eventually
+    be derived by "evaluating" (calling) the function.
 
-    Serves as a placeholder for a value that should eventually be derived by 
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, context, container):
+    def __evaluate__(self, container_holder):
+        """
+        Evaluates the function if possible. If impossible, raises
+        :class:`CannotEvaluateFunctionException` (do not just return None).
+
+        :rtype: Evaluation (or any object with ``value`` and ``final`` 
properties)
+        """
+
         raise NotImplementedError
 
     def __deepcopy__(self, memo):
         # Circumvent cloning in order to maintain our state
         return self
+
+
+class Evaluation(object):
+    """
+    A wrapper for an evaluated :class:`Function` value.
+    """
+
+    def __init__(self, value, final=False):
+        self.value = value
+        self.final = final
+
+
+def evaluate(value, container_holder, report_issues=False):
+    """
+    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.
+    """
+
+    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

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/6f349dd7/aria/modeling/mixins.py
----------------------------------------------------------------------
diff --git a/aria/modeling/mixins.py b/aria/modeling/mixins.py
index e6db5a3..38c812d 100644
--- a/aria/modeling/mixins.py
+++ b/aria/modeling/mixins.py
@@ -124,7 +124,7 @@ class InstanceModelMixin(ModelMixin):
     def validate(self):
         pass
 
-    def coerce_values(self, container, report_issues):
+    def coerce_values(self, report_issues):
         pass
 
     def dump(self):

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/6f349dd7/aria/modeling/service_common.py
----------------------------------------------------------------------
diff --git a/aria/modeling/service_common.py b/aria/modeling/service_common.py
index 01458ad..9539979 100644
--- a/aria/modeling/service_common.py
+++ b/aria/modeling/service_common.py
@@ -30,7 +30,7 @@ from ..utils import collections, formatting, console
 from .mixins import InstanceModelMixin, TemplateModelMixin
 from . import (
     relationship,
-    utils
+    functions
 )
 
 
@@ -53,10 +53,165 @@ class ParameterBase(TemplateModelMixin):
 
     name = Column(Text)
     type_name = Column(Text)
-    value = Column(PickleType)
+    _value = Column(PickleType)
     description = Column(Text)
 
     @property
+    def value(self):
+        value = self._value
+        if value is not None:
+            evaluation = functions.evaluate(value, self)
+            if evaluation is not None:
+                print '>>> EVALUATED'
+                value = evaluation.value
+        return value
+
+    @value.setter
+    def value(self, value):
+        self._value = value
+
+    @property
+    def container(self):
+        """
+        The container for this parameter, which would be another model.
+
+        *All* parameters should have a container model. In case this property 
method fails to find
+        it, it will raise a ValueError, which should signify an orphaned 
parameter.
+        """
+
+        def get_interface_container(interface):
+            # Node
+            if interface.node:
+                return interface.node
+            elif interface.relationship:
+                return interface.relationship.source_node
+            # Group
+            elif interface.group:
+                return interface.group
+            raise ValueError('interface parameter does not have a container: 
{0}'
+                             .format(self.name))
+
+        def get_interface_template_container(interface_template):
+            # NodeTemplate
+            if interface_template.node_template:
+                return interface_template.node_template
+            elif interface_template.relationship_template:
+                relationship_template = 
interface_template.relationship_template
+                requirement_template = 
relationship_template.requirement_template
+                return requirement_template.node_template
+            # GroupTemplate
+            elif interface_template.group_template:
+                return interface_template.group_template
+            raise ValueError('interface_template parameter does not have a 
container: {0}'
+                             .format(self.name))
+
+        # Node
+        if self.properties_nodes:
+            return self.properties_nodes[0]
+        elif self.attributes_nodes:
+            return self.attributes_nodes[0]
+        elif self.properties_capabilities:
+            capability = self.properties_capabilities[0]
+            return capability.node
+        elif self.properties_artifacts:
+            artifact = self.properties_artifacts[0]
+            return artifact.node
+        elif self.properties_relationships:
+            relationship = self.properties_relationships[0]
+            return relationship.source_node
+        # Group
+        elif self.properties_groups:
+            return self.properties_groups[0]
+        # Policy
+        elif self.properties_policies:
+            return self.properties_policies[0]
+        # Service
+        elif self.inputs_services:
+            return self.inputs_services[0]
+        elif self.outputs_services:
+            return self.outputs_services[0]
+        # Execution
+        elif self.inputs_executions:
+            return self.inputs_executions[0]
+        # Node or Group
+        elif self.inputs_interfaces:
+            interface = self.inputs_interfaces[0]
+            return get_interface_container(interface)
+        elif self.inputs_operations:
+            operation = self.inputs_operations[0]
+            # Node or Group
+            if operation.interface:
+                return get_interface_container(operation.interface)
+            # Service
+            elif operation.service:
+                return operation.service
+            raise ValueError('operation parameter does not have a container: 
{0}'
+                             .format(self.name))
+
+        # NodeTemplate
+        elif self.properties_node_templates:
+            return self.properties_node_templates[0]
+        elif self.attributes_node_templates:
+            return self.attributes_node_templates[0]
+        elif self.properties_capability_templates:
+            capability_template = self.properties_capability_templates[0]
+            return capability_template.node_template
+        elif self.properties_artifact_templates:
+            artifact_template = self.properties_artifact_templates[0]
+            return artifact_template.node_template
+        elif self.properties_relationship_templates:
+            relationship_template = self.properties_relationship_templates[0]
+            requirement_template = relationship_template.requirement_template
+            return requirement_template.node_template
+        # GroupTemplate
+        elif self.properties_group_templates:
+            return self.properties_group_templates[0]
+        # PolicyTemplate
+        elif self.properties_policy_templates:
+            return self.properties_policy_templates[0]
+        # ServiceTemplate
+        elif self.inputs_service_templates:
+            return self.inputs_service_templates[0]
+        elif self.outputs_service_templates:
+            return self.outputs_service_templates[0]
+        # NodeTemplate or GroupTemplate
+        elif self.inputs_interface_templates:
+            interface_template = self.inputs_interface_templates[0]
+            return get_interface_template_container(interface_template)
+        elif self.inputs_operation_templates:
+            operation_template = self.inputs_operation_templates[0]
+            # NodeTemplate or GroupTemplate
+            if operation_template.interface_template:
+                return 
get_interface_template_container(operation_template.interface_template)
+            # ServiceTemplate
+            elif operation_template.service_template:
+                return operation_template.service_template
+            raise ValueError('operation_template parameter does not have a 
container: {0}'
+                             .format(self.name))
+
+        raise ValueError('parameter does not have a container: 
{0}'.format(self.name))
+
+    @property
+    def service(self):
+        from . import models
+        container = self.container
+        if isinstance(container, models.Service):
+            return container
+        elif hasattr(container, 'service'):
+            return container.service
+        return None
+
+    @property
+    def service_template(self):
+        from . import models
+        container = self.container
+        if isinstance(container, models.ServiceTemplate):
+            return container
+        elif hasattr(container, 'service_template'):
+            return container.service_template
+        return None
+
+    @property
     def as_raw(self):
         return collections.OrderedDict((
             ('name', self.name),
@@ -68,15 +223,16 @@ class ParameterBase(TemplateModelMixin):
         from . import models
         return models.Parameter(name=self.name,
                                 type_name=self.type_name,
-                                value=self.value,
+                                _value=self._value,
                                 description=self.description)
 
-    def coerce_values(self, container, report_issues):
-        if self.value is not None:
-            final, value = utils.coerce_value(container, self.value,
-                                              report_issues)
-            # TODO: final?
-            self.value = value
+    def coerce_values(self, report_issues):
+        value = self._value
+        if value is not None:
+            evaluation = functions.evaluate(value, self, report_issues)
+            if (evaluation is not None) and evaluation.final:
+                # A final evaluation can safely replace the existing value
+                self._value = evaluation.value
 
     def dump(self):
         context = ConsumptionContext.get_thread_local()
@@ -212,7 +368,7 @@ class TypeBase(InstanceModelMixin):
         self._append_raw_children(types)
         return types
 
-    def coerce_values(self, container, report_issues):
+    def coerce_values(self, report_issues):
         pass
 
     def dump(self):
@@ -261,7 +417,7 @@ class MetadataBase(TemplateModelMixin):
             ('name', self.name),
             ('value', self.value)))
 
-    def coerce_values(self, container, report_issues):
+    def coerce_values(self, report_issues):
         pass
 
     def instantiate(self, container):

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/6f349dd7/aria/modeling/service_instance.py
----------------------------------------------------------------------
diff --git a/aria/modeling/service_instance.py 
b/aria/modeling/service_instance.py
index b293535..3ef91d6 100644
--- a/aria/modeling/service_instance.py
+++ b/aria/modeling/service_instance.py
@@ -261,16 +261,16 @@ class ServiceBase(InstanceModelMixin):
         utils.validate_dict_values(self.outputs)
         utils.validate_dict_values(self.workflows)
 
-    def coerce_values(self, container, report_issues):
-        utils.coerce_dict_values(container, self.meta_data, report_issues)
-        utils.coerce_dict_values(container, self.nodes, report_issues)
-        utils.coerce_dict_values(container, self.groups, report_issues)
-        utils.coerce_dict_values(container, self.policies, report_issues)
+    def coerce_values(self, report_issues):
+        utils.coerce_dict_values(self.meta_data, report_issues)
+        utils.coerce_dict_values(self.nodes, report_issues)
+        utils.coerce_dict_values(self.groups, report_issues)
+        utils.coerce_dict_values(self.policies, report_issues)
         if self.substitution is not None:
-            self.substitution.coerce_values(container, report_issues)
-        utils.coerce_dict_values(container, self.inputs, report_issues)
-        utils.coerce_dict_values(container, self.outputs, report_issues)
-        utils.coerce_dict_values(container, self.workflows, report_issues)
+            self.substitution.coerce_values(report_issues)
+        utils.coerce_dict_values(self.inputs, report_issues)
+        utils.coerce_dict_values(self.outputs, report_issues)
+        utils.coerce_dict_values(self.workflows, report_issues)
 
     def dump(self):
         context = ConsumptionContext.get_thread_local()
@@ -678,13 +678,13 @@ class NodeBase(InstanceModelMixin):
         utils.validate_dict_values(self.capabilities)
         utils.validate_list_values(self.outbound_relationships)
 
-    def coerce_values(self, container, report_issues):
-        utils.coerce_dict_values(self, self.properties, report_issues)
-        utils.coerce_dict_values(self, self.attributes, report_issues)
-        utils.coerce_dict_values(self, self.interfaces, report_issues)
-        utils.coerce_dict_values(self, self.artifacts, report_issues)
-        utils.coerce_dict_values(self, self.capabilities, report_issues)
-        utils.coerce_list_values(self, self.outbound_relationships, 
report_issues)
+    def coerce_values(self, report_issues):
+        utils.coerce_dict_values(self.properties, report_issues)
+        utils.coerce_dict_values(self.attributes, report_issues)
+        utils.coerce_dict_values(self.interfaces, report_issues)
+        utils.coerce_dict_values(self.artifacts, report_issues)
+        utils.coerce_dict_values(self.capabilities, report_issues)
+        utils.coerce_list_values(self.outbound_relationships, report_issues)
 
     def dump(self):
         context = ConsumptionContext.get_thread_local()
@@ -808,9 +808,9 @@ class GroupBase(InstanceModelMixin):
         utils.validate_dict_values(self.properties)
         utils.validate_dict_values(self.interfaces)
 
-    def coerce_values(self, container, report_issues):
-        utils.coerce_dict_values(container, self.properties, report_issues)
-        utils.coerce_dict_values(container, self.interfaces, report_issues)
+    def coerce_values(self, report_issues):
+        utils.coerce_dict_values(self.properties, report_issues)
+        utils.coerce_dict_values(self.interfaces, report_issues)
 
     def dump(self):
         context = ConsumptionContext.get_thread_local()
@@ -927,8 +927,8 @@ class PolicyBase(InstanceModelMixin):
     def validate(self):
         utils.validate_dict_values(self.properties)
 
-    def coerce_values(self, container, report_issues):
-        utils.coerce_dict_values(container, self.properties, report_issues)
+    def coerce_values(self, report_issues):
+        utils.coerce_dict_values(self.properties, report_issues)
 
     def dump(self):
         context = ConsumptionContext.get_thread_local()
@@ -1028,8 +1028,8 @@ class SubstitutionBase(InstanceModelMixin):
     def validate(self):
         utils.validate_dict_values(self.mappings)
 
-    def coerce_values(self, container, report_issues):
-        utils.coerce_dict_values(container, self.mappings, report_issues)
+    def coerce_values(self, report_issues):
+        utils.coerce_dict_values(self.mappings, report_issues)
 
     def dump(self):
         context = ConsumptionContext.get_thread_local()
@@ -1132,7 +1132,7 @@ class SubstitutionMappingBase(InstanceModelMixin):
         return collections.OrderedDict((
             ('name', self.name)))
 
-    def coerce_values(self, container, report_issues):
+    def coerce_values(self, report_issues):
         pass
 
     def validate(self):
@@ -1320,9 +1320,9 @@ class RelationshipBase(InstanceModelMixin):
         utils.validate_dict_values(self.properties)
         utils.validate_dict_values(self.interfaces)
 
-    def coerce_values(self, container, report_issues):
-        utils.coerce_dict_values(container, self.properties, report_issues)
-        utils.coerce_dict_values(container, self.interfaces, report_issues)
+    def coerce_values(self, report_issues):
+        utils.coerce_dict_values(self.properties, report_issues)
+        utils.coerce_dict_values(self.interfaces, report_issues)
 
     def dump(self):
         context = ConsumptionContext.get_thread_local()
@@ -1460,8 +1460,8 @@ class CapabilityBase(InstanceModelMixin):
     def validate(self):
         utils.validate_dict_values(self.properties)
 
-    def coerce_values(self, container, report_issues):
-        utils.coerce_dict_values(container, self.properties, report_issues)
+    def coerce_values(self, report_issues):
+        utils.coerce_dict_values(self.properties, report_issues)
 
     def dump(self):
         context = ConsumptionContext.get_thread_local()
@@ -1607,9 +1607,9 @@ class InterfaceBase(InstanceModelMixin):
         utils.validate_dict_values(self.inputs)
         utils.validate_dict_values(self.operations)
 
-    def coerce_values(self, container, report_issues):
-        utils.coerce_dict_values(container, self.inputs, report_issues)
-        utils.coerce_dict_values(container, self.operations, report_issues)
+    def coerce_values(self, report_issues):
+        utils.coerce_dict_values(self.inputs, report_issues)
+        utils.coerce_dict_values(self.operations, report_issues)
 
     def dump(self):
         context = ConsumptionContext.get_thread_local()
@@ -1774,8 +1774,8 @@ class OperationBase(InstanceModelMixin):
         # TODO must be associated with interface or service
         utils.validate_dict_values(self.inputs)
 
-    def coerce_values(self, container, report_issues):
-        utils.coerce_dict_values(container, self.inputs, report_issues)
+    def coerce_values(self, report_issues):
+        utils.coerce_dict_values(self.inputs, report_issues)
 
     def dump(self):
         context = ConsumptionContext.get_thread_local()
@@ -1914,8 +1914,8 @@ class ArtifactBase(InstanceModelMixin):
     def validate(self):
         utils.validate_dict_values(self.properties)
 
-    def coerce_values(self, container, report_issues):
-        utils.coerce_dict_values(container, self.properties, report_issues)
+    def coerce_values(self, report_issues):
+        utils.coerce_dict_values(self.properties, report_issues)
 
     def dump(self):
         context = ConsumptionContext.get_thread_local()

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/6f349dd7/aria/modeling/service_template.py
----------------------------------------------------------------------
diff --git a/aria/modeling/service_template.py 
b/aria/modeling/service_template.py
index 48eeb83..ec9bed1 100644
--- a/aria/modeling/service_template.py
+++ b/aria/modeling/service_template.py
@@ -346,16 +346,16 @@ class ServiceTemplateBase(TemplateModelMixin):
         if self.artifact_types is not None:
             self.artifact_types.validate()
 
-    def coerce_values(self, container, report_issues):
-        utils.coerce_dict_values(container, self.meta_data, report_issues)
-        utils.coerce_dict_values(container, self.node_templates, report_issues)
-        utils.coerce_dict_values(container, self.group_templates, 
report_issues)
-        utils.coerce_dict_values(container, self.policy_templates, 
report_issues)
+    def coerce_values(self, report_issues):
+        utils.coerce_dict_values(self.meta_data, report_issues)
+        utils.coerce_dict_values(self.node_templates, report_issues)
+        utils.coerce_dict_values(self.group_templates, report_issues)
+        utils.coerce_dict_values(self.policy_templates, report_issues)
         if self.substitution_template is not None:
-            self.substitution_template.coerce_values(container, report_issues)
-        utils.coerce_dict_values(container, self.inputs, report_issues)
-        utils.coerce_dict_values(container, self.outputs, report_issues)
-        utils.coerce_dict_values(container, self.workflow_templates, 
report_issues)
+            self.substitution_template.coerce_values(report_issues)
+        utils.coerce_dict_values(self.inputs, report_issues)
+        utils.coerce_dict_values(self.outputs, report_issues)
+        utils.coerce_dict_values(self.workflow_templates, report_issues)
 
     def dump(self):
         context = ConsumptionContext.get_thread_local()
@@ -585,13 +585,13 @@ class NodeTemplateBase(TemplateModelMixin):
         utils.validate_dict_values(self.capability_templates)
         utils.validate_list_values(self.requirement_templates)
 
-    def coerce_values(self, container, report_issues):
-        utils.coerce_dict_values(self, self.properties, report_issues)
-        utils.coerce_dict_values(self, self.attributes, report_issues)
-        utils.coerce_dict_values(self, self.interface_templates, report_issues)
-        utils.coerce_dict_values(self, self.artifact_templates, report_issues)
-        utils.coerce_dict_values(self, self.capability_templates, 
report_issues)
-        utils.coerce_list_values(self, self.requirement_templates, 
report_issues)
+    def coerce_values(self, report_issues):
+        utils.coerce_dict_values(self.properties, report_issues)
+        utils.coerce_dict_values(self.attributes, report_issues)
+        utils.coerce_dict_values(self.interface_templates, report_issues)
+        utils.coerce_dict_values(self.artifact_templates, report_issues)
+        utils.coerce_dict_values(self.capability_templates, report_issues)
+        utils.coerce_list_values(self.requirement_templates, report_issues)
 
     def dump(self):
         context = ConsumptionContext.get_thread_local()
@@ -731,9 +731,9 @@ class GroupTemplateBase(TemplateModelMixin):
         utils.validate_dict_values(self.properties)
         utils.validate_dict_values(self.interface_templates)
 
-    def coerce_values(self, container, report_issues):
-        utils.coerce_dict_values(self, self.properties, report_issues)
-        utils.coerce_dict_values(self, self.interface_templates, report_issues)
+    def coerce_values(self, report_issues):
+        utils.coerce_dict_values(self.properties, report_issues)
+        utils.coerce_dict_values(self.interface_templates, report_issues)
 
     def dump(self):
         context = ConsumptionContext.get_thread_local()
@@ -862,8 +862,8 @@ class PolicyTemplateBase(TemplateModelMixin):
     def validate(self):
         utils.validate_dict_values(self.properties)
 
-    def coerce_values(self, container, report_issues):
-        utils.coerce_dict_values(self, self.properties, report_issues)
+    def coerce_values(self, report_issues):
+        utils.coerce_dict_values(self.properties, report_issues)
 
     def dump(self):
         context = ConsumptionContext.get_thread_local()
@@ -956,8 +956,8 @@ class SubstitutionTemplateBase(TemplateModelMixin):
     def validate(self):
         utils.validate_dict_values(self.mappings)
 
-    def coerce_values(self, container, report_issues):
-        utils.coerce_dict_values(self, self.mappings, report_issues)
+    def coerce_values(self, report_issues):
+        utils.coerce_dict_values(self.mappings, report_issues)
 
     def dump(self):
         context = ConsumptionContext.get_thread_local()
@@ -1060,7 +1060,7 @@ class SubstitutionTemplateMappingBase(TemplateModelMixin):
         return collections.OrderedDict((
             ('name', self.name)))
 
-    def coerce_values(self, container, report_issues):
+    def coerce_values(self, report_issues):
         pass
 
     def instantiate(self, container):
@@ -1295,9 +1295,9 @@ class RequirementTemplateBase(TemplateModelMixin):
         if self.relationship_template:
             self.relationship_template.validate()
 
-    def coerce_values(self, container, report_issues):
+    def coerce_values(self, report_issues):
         if self.relationship_template is not None:
-            self.relationship_template.coerce_values(container, report_issues)
+            self.relationship_template.coerce_values(report_issues)
 
     def dump(self):
         context = ConsumptionContext.get_thread_local()
@@ -1428,9 +1428,9 @@ class RelationshipTemplateBase(TemplateModelMixin):
         utils.validate_dict_values(self.properties)
         utils.validate_dict_values(self.interface_templates)
 
-    def coerce_values(self, container, report_issues):
-        utils.coerce_dict_values(self, self.properties, report_issues)
-        utils.coerce_dict_values(self, self.interface_templates, report_issues)
+    def coerce_values(self, report_issues):
+        utils.coerce_dict_values(self.properties, report_issues)
+        utils.coerce_dict_values(self.interface_templates, report_issues)
 
     def dump(self):
         context = ConsumptionContext.get_thread_local()
@@ -1585,8 +1585,8 @@ class CapabilityTemplateBase(TemplateModelMixin):
     def validate(self):
         utils.validate_dict_values(self.properties)
 
-    def coerce_values(self, container, report_issues):
-        utils.coerce_dict_values(self, self.properties, report_issues)
+    def coerce_values(self, report_issues):
+        utils.coerce_dict_values(self.properties, report_issues)
 
     def dump(self):
         context = ConsumptionContext.get_thread_local()
@@ -1739,9 +1739,9 @@ class InterfaceTemplateBase(TemplateModelMixin):
         utils.validate_dict_values(self.inputs)
         utils.validate_dict_values(self.operation_templates)
 
-    def coerce_values(self, container, report_issues):
-        utils.coerce_dict_values(container, self.inputs, report_issues)
-        utils.coerce_dict_values(container, self.operation_templates, 
report_issues)
+    def coerce_values(self, report_issues):
+        utils.coerce_dict_values(self.inputs, report_issues)
+        utils.coerce_dict_values(self.operation_templates, report_issues)
 
     def dump(self):
         context = ConsumptionContext.get_thread_local()
@@ -1893,7 +1893,7 @@ class OperationTemplateBase(TemplateModelMixin):
                 plugin = None
                 implementation = None
         else:
-            # using the execution plugin
+            # Using the execution plugin
             plugin = None
             implementation = self.implementation
 
@@ -1914,8 +1914,8 @@ class OperationTemplateBase(TemplateModelMixin):
     def validate(self):
         utils.validate_dict_values(self.inputs)
 
-    def coerce_values(self, container, report_issues):
-        utils.coerce_dict_values(container, self.inputs, report_issues)
+    def coerce_values(self, report_issues):
+        utils.coerce_dict_values(self.inputs, report_issues)
 
     def dump(self):
         context = ConsumptionContext.get_thread_local()
@@ -2062,8 +2062,8 @@ class ArtifactTemplateBase(TemplateModelMixin):
     def validate(self):
         utils.validate_dict_values(self.properties)
 
-    def coerce_values(self, container, report_issues):
-        utils.coerce_dict_values(container, self.properties, report_issues)
+    def coerce_values(self, report_issues):
+        utils.coerce_dict_values(self.properties, report_issues)
 
     def dump(self):
         context = ConsumptionContext.get_thread_local()
@@ -2139,7 +2139,7 @@ class PluginSpecificationBase(TemplateModelMixin):
             ('version', self.version),
             ('enabled', self.enabled)))
 
-    def coerce_values(self, container, report_issues):
+    def coerce_values(self, report_issues):
         pass
 
     def resolve(self, model_storage):

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/6f349dd7/aria/modeling/utils.py
----------------------------------------------------------------------
diff --git a/aria/modeling/utils.py b/aria/modeling/utils.py
index 7be20d6..c786965 100644
--- a/aria/modeling/utils.py
+++ b/aria/modeling/utils.py
@@ -19,8 +19,6 @@ from StringIO import StringIO
 
 from . import exceptions
 from ..parser.consumption import ConsumptionContext
-from ..parser.exceptions import InvalidValueError
-from ..utils.collections import OrderedDict
 from ..utils.console import puts
 from ..utils.type import validate_value_type
 
@@ -108,52 +106,17 @@ def _merge_and_validate_inputs(inputs, template_inputs):
     return merged_inputs
 
 
-def coerce_value(container, value, report_issues=False):
-    final = True
-
-    if isinstance(value, list):
-        coerced_list = []
-        for v in value:
-            child_final, v = coerce_value(container, v, report_issues)
-            if not child_final:
-                final = False
-            coerced_list.append(v)
-        value = coerced_list
-    elif isinstance(value, dict):
-        coerced_dict = OrderedDict()
-        for k, v in value.iteritems():
-            child_final, v = coerce_value(container, v, report_issues)
-            if not child_final:
-                final = False
-            coerced_dict[k] = v
-        value = coerced_dict
-    elif hasattr(value, '_evaluate'):
-        context = ConsumptionContext.get_thread_local()
-        try:
-            final, value = value._evaluate(context, container)
-            child_final, value = coerce_value(container, value, report_issues)
-            if not child_final:
-                final = False
-        except exceptions.CannotEvaluateFunctionException:
-            final = False
-        except InvalidValueError as e:
-            if report_issues:
-                context.validation.report(e.issue)
-
-    return final, value
-
-
-def coerce_dict_values(container, the_dict, report_issues=False):
+def coerce_dict_values(the_dict, report_issues=False):
     if not the_dict:
         return
-    coerce_list_values(container, the_dict.itervalues(), report_issues)
+    coerce_list_values(the_dict.itervalues(), report_issues)
 
 
-def coerce_list_values(container, the_list, report_issues=False):
+def coerce_list_values(the_list, report_issues=False):
     if not the_list:
         return
     for value in the_list:
-        value.coerce_values(container, report_issues)
+        value.coerce_values(report_issues)
 
 
 def validate_dict_values(the_dict):

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/6f349dd7/aria/parser/consumption/__init__.py
----------------------------------------------------------------------
diff --git a/aria/parser/consumption/__init__.py 
b/aria/parser/consumption/__init__.py
index 8f6d2b6..97b3db4 100644
--- a/aria/parser/consumption/__init__.py
+++ b/aria/parser/consumption/__init__.py
@@ -28,9 +28,11 @@ from .modeling import (
     Types,
     ServiceInstance,
     FindHosts,
+    ValidateServiceInstance,
     ConfigureOperations,
     SatisfyRequirements,
-    ValidateCapabilities
+    ValidateCapabilities,
+    CoerceServiceInstanceValues
 )
 from .inputs import Inputs
 
@@ -45,7 +47,10 @@ __all__ = (
     'ServiceTemplate',
     'Types',
     'ServiceInstance',
-    'Inputs',
+    'FindHosts',
+    'ValidateServiceInstance',
+    'ConfigureOperations',
     'SatisfyRequirements',
     'ValidateCapabilities'
+    'CoerceServiceInstanceValues'
 )

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/6f349dd7/aria/parser/consumption/modeling.py
----------------------------------------------------------------------
diff --git a/aria/parser/consumption/modeling.py 
b/aria/parser/consumption/modeling.py
index 771fd7f..44027b9 100644
--- a/aria/parser/consumption/modeling.py
+++ b/aria/parser/consumption/modeling.py
@@ -42,7 +42,7 @@ class CoerceServiceTemplateValues(Consumer):
     """
 
     def consume(self):
-        self.context.modeling.template.coerce_values(None, True)
+        self.context.modeling.template.coerce_values(True)
 
 
 class ValidateServiceTemplate(Consumer):
@@ -116,7 +116,7 @@ class CoerceServiceInstanceValues(Consumer):
     """
 
     def consume(self):
-        self.context.modeling.instance.coerce_values(None, True)
+        self.context.modeling.instance.coerce_values(True)
 
 
 class ValidateServiceInstance(Consumer):

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/6f349dd7/extensions/aria_extension_tosca/simple_v1_0/functions.py
----------------------------------------------------------------------
diff --git a/extensions/aria_extension_tosca/simple_v1_0/functions.py 
b/extensions/aria_extension_tosca/simple_v1_0/functions.py
index 125452e..c698953 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/functions.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/functions.py
@@ -18,10 +18,12 @@ from cStringIO import StringIO
 from aria.utils.collections import FrozenList
 from aria.utils.formatting import as_raw, safe_repr
 from aria.parser import dsl_specification
+from aria.parser.consumption import ConsumptionContext
 from aria.parser.exceptions import InvalidValueError
 from aria.parser.validation import Issue
 from aria.modeling.exceptions import CannotEvaluateFunctionException
-from aria.modeling.functions import Function
+from aria.modeling.models import Parameter
+from aria.modeling.functions import (Function, Evaluation)
 
 
 #
@@ -59,16 +61,16 @@ class Concat(Function):
             string_expressions.append(string_expression)
         return {'concat': string_expressions}
 
-    def _evaluate(self, context, container):
+    def __evaluate__(self, container_holder):
         final = True
         value = StringIO()
         for e in self.string_expressions:
-            if hasattr(e, '_evaluate'):
-                child_final, e = e._evaluate(context, container)
-                if not child_final:
+            if hasattr(e, '__evaluate__'):
+                e = e.__evaluate__(container_holder)
+                if not e.final:
                     final = False
             value.write(str(e))
-        return final, value.getvalue()
+        return Evaluation(value.getvalue(), final)
 
 
 @dsl_specification('4.3.2', 'tosca-simple-1.0')
@@ -104,10 +106,10 @@ class Token(Function):
             string_of_token_chars = as_raw(string_of_token_chars)
         return {'token': [string_with_tokens, string_of_token_chars, 
self.substring_index]}
 
-    def _evaluate(self, context, container):
+    def __evaluate__(self, container_holder):
         string_with_tokens = self.string_with_tokens
-        if hasattr(string_with_tokens, '_evaluate'):
-            string_with_tokens = string_with_tokens._evaluate(context, 
container) # pylint: disable=no-member
+        if hasattr(string_with_tokens, '__evaluate__'):
+            string_with_tokens = 
string_with_tokens.__evaluate__(container_holder) # pylint: disable=no-member
         # TODO
 
 #
@@ -141,13 +143,18 @@ class GetInput(Function):
     def as_raw(self):
         return {'get_input': as_raw(self.input_property_name)}
 
-    def _evaluate(self, context, container): # pylint: disable=unused-argument
-        if not context.modeling.instance:
+    def __evaluate__(self, container_holder):
+        if not isinstance(container_holder, Parameter):
             raise CannotEvaluateFunctionException()
-        the_input = context.modeling.instance.inputs.get(
-            self.input_property_name,
-            context.modeling.template.inputs.get(self.input_property_name))
-        return True, (as_raw(the_input.value) if the_input is not None else 
None)
+
+        service = container_holder.service
+        if service is None:
+            raise CannotEvaluateFunctionException()
+            
+        the_input = service.inputs.get(self.input_property_name)
+        if the_input is not None:
+            the_input = as_raw(the_input.value)
+        return Evaluation(the_input, False)
 
 
 @dsl_specification('4.4.2', 'tosca-simple-1.0')
@@ -175,8 +182,9 @@ class GetProperty(Function):
     def as_raw(self):
         return {'get_property': [self.modelable_entity_name] + 
self.nested_property_name_or_index}
 
-    def _evaluate(self, context, container):
-        modelable_entities = get_modelable_entities(context, container, 
self.locator,
+    def __evaluate__(self, container_holder):
+        context = ConsumptionContext.get_thread_local()
+        modelable_entities = get_modelable_entities(context, container_holder, 
self.locator,
                                                     self.modelable_entity_name)
         req_or_cap_name = self.nested_property_name_or_index[0]
 
@@ -203,10 +211,10 @@ class GetProperty(Function):
                 properties = modelable_entity.properties
                 nested_property_name_or_index = 
self.nested_property_name_or_index
 
-            found, final, value = get_modelable_entity_parameter(context, 
modelable_entity, properties,
-                                                          
nested_property_name_or_index)
-            if found:
-                return final, as_raw(value.value)
+            evaluation = get_modelable_entity_parameter(context, 
modelable_entity, properties,
+                                                        
nested_property_name_or_index)
+            if evaluation is not None:
+                return evaluation
 
         raise InvalidValueError(
             'function "get_property" could not find "{0}" in modelable entity 
"{1}"'
@@ -243,25 +251,24 @@ class GetAttribute(Function):
     def as_raw(self):
         return {'get_attribute': [self.modelable_entity_name] + 
self.nested_attribute_name_or_index}
 
-    def _evaluate(self, context, container): # pylint: 
disable=no-self-use,unused-argument
-        raise CannotEvaluateFunctionException()
-        
-        modelable_entities = get_modelable_entities(context, container, 
self.locator,
+    def __evaluate__(self, container_holder):
+        modelable_entities = get_modelable_entities(container_holder, 
self.locator,
                                                     self.modelable_entity_name)
         for modelable_entity in modelable_entities:
             attributes = modelable_entity.attributes
             nested_attribute_name_or_index = 
self.nested_attribute_name_or_index
-
-            found, value = get_modelable_entity_parameter(context, 
modelable_entity, attributes,
-                                                          
nested_attribute_name_or_index)
-            if found:
-                return False, as_raw(value.value)
+            evaluation = get_modelable_entity_parameter(modelable_entity, 
attributes,
+                                                        
nested_attribute_name_or_index)
+            if evaluation is not None:
+                evaluation.final = False # We never return final evaluations! 
+                return evaluation
 
         raise InvalidValueError(
             'function "get_attribute" could not find "{0}" in modelable entity 
"{1}"'
             .format('.'.join(self.nested_attribute_name_or_index), 
self.modelable_entity_name),
             locator=self.locator)
 
+
 #
 # Operation
 #
@@ -340,7 +347,7 @@ class GetNodesOfType(Function):
             node_type_name = as_raw(node_type_name)
         return {'get_nodes_of_type': node_type_name}
 
-    def _evaluate(self, context, container):
+    def __evaluate__(self, container):
         pass
 
 
@@ -477,20 +484,20 @@ def parse_self(presentation):
 
 
 @dsl_specification('4.1', 'tosca-simple-1.0')
-def get_modelable_entities(context, container, locator, modelable_entity_name):
+def get_modelable_entities(container_holder, locator, modelable_entity_name):
     """
     The following keywords MAY be used in some TOSCA function in place of a 
TOSCA Node or
     Relationship Template name.
     """
 
     if modelable_entity_name == 'SELF':
-        return get_self(context, container)
+        return get_self(container_holder)
     elif modelable_entity_name == 'HOST':
-        return get_hosts(context, container)
+        return get_hosts(container_holder)
     elif modelable_entity_name == 'SOURCE':
-        return get_source(context, container)
+        return get_source(container_holder)
     elif modelable_entity_name == 'TARGET':
-        return get_target(context, container)
+        return get_target(container_holder)
     elif isinstance(modelable_entity_name, basestring):
         node_templates = \
             context.presentation.get('service_template', 'topology_template', 
'node_templates') \
@@ -509,16 +516,16 @@ def get_modelable_entities(context, container, locator, 
modelable_entity_name):
                             locator=locator)
 
 
-def get_self(context, container): # pylint: disable=unused-argument
+def get_self(container_holder):
     """
     A TOSCA orchestrator will interpret this keyword as the Node or 
Relationship Template instance
     that contains the function at the time the function is evaluated.
     """
 
-    return [container]
+    return [container_holder.container]
 
 
-def get_hosts(context, container): # pylint: disable=unused-argument
+def get_hosts(container_holder): # pylint: disable=unused-argument
     """
     A TOSCA orchestrator will interpret this keyword to refer to the all nodes 
that "host" the node
     using this reference (i.e., as identified by its HostedOn relationship).
@@ -533,7 +540,7 @@ def get_hosts(context, container): # pylint: 
disable=unused-argument
     return []
 
 
-def get_source(context, container): # pylint: disable=unused-argument
+def get_source(container_holder): # pylint: disable=unused-argument
     """
     A TOSCA orchestrator will interpret this keyword as the Node Template 
instance that is at the
     source end of the relationship that contains the referencing function.
@@ -542,15 +549,14 @@ def get_source(context, container): # pylint: 
disable=unused-argument
     return []
 
 
-def get_target(context, container): # pylint: disable=unused-argument
+def get_target(container_holder): # pylint: disable=unused-argument
     """
     A TOSCA orchestrator will interpret this keyword as the Node Template 
instance that is at the
     target end of the relationship that contains the referencing function.
     """
 
 
-def get_modelable_entity_parameter(context, modelable_entity, parameters,
-                                   nested_parameter_name_or_index):
+def get_modelable_entity_parameter(modelable_entity, parameters, 
nested_parameter_name_or_index):
     if not parameters:
         return False, True, None
 
@@ -561,16 +567,17 @@ def get_modelable_entity_parameter(context, 
modelable_entity, parameters,
     for name in nested_parameter_name_or_index:
         if (isinstance(value, dict) and (name in value)) \
             or (isinstance(value, list) and name < len(list)):
-            value = value[name]
-            if hasattr(value, '_evaluate'):
-                child_final, value = value._evaluate(context, modelable_entity)
-                if not child_final:
+            value = value[name].value
+            if hasattr(value, '__evaluate__'):
+                evaluation = value.__evaluate__(modelable_entity)
+                value = evaluation.value
+                if not evaluation.final:
                     final = False
         else:
             found = False
             break
 
-    return found, final, value
+    return Evaluation(value, final) if found else None
 
 
 def invalid_modelable_entity_name(name, index, value, locator, contexts):

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/6f349dd7/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py
----------------------------------------------------------------------
diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py 
b/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py
index 945f795..c7e1a58 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py
@@ -591,8 +591,8 @@ def create_constraint_clause_lambda(context, node_filter, 
constraint_clause, pro
     def coerce_constraint(constraint, container):
         constraint = coerce_value(context, node_filter, the_type, None, None, 
constraint,
                                   constraint_key) if the_type is not None else 
constraint
-        if hasattr(constraint, '_evaluate'):
-            constraint = constraint._evaluate(context, container)
+        if hasattr(constraint, '__evaluate__'):
+            constraint = constraint.__evaluate__(container).value
         return constraint
 
     def get_value(node_type):

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/6f349dd7/extensions/aria_extension_tosca/simple_v1_0/modeling/data_types.py
----------------------------------------------------------------------
diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/data_types.py 
b/extensions/aria_extension_tosca/simple_v1_0/modeling/data_types.py
index 6c7b780..8bf45e0 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/modeling/data_types.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/data_types.py
@@ -371,6 +371,8 @@ def coerce_value(context, presentation, the_type, 
entry_schema, constraints, val
     If the extension is present, we will delegate to that hook.
     """
 
+    # TODO: should support models as well as presentations
+
     is_function, func = get_function(context, presentation, value)
     if is_function:
         return func

Reply via email to