Rebase, work to use models
Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/9de5d979 Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/9de5d979 Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/9de5d979 Branch: refs/heads/ARIA-139-attributes Commit: 9de5d9799d2e5119a4b081993a02abf1f65f79a2 Parents: 8e5a1ec Author: Tal Liron <[email protected]> Authored: Wed Apr 19 20:07:33 2017 -0500 Committer: Tal Liron <[email protected]> Committed: Wed Apr 19 20:07:33 2017 -0500 ---------------------------------------------------------------------- aria/modeling/service_common.py | 32 ++++++-- aria/modeling/service_instance.py | 8 ++ aria/modeling/service_template.py | 16 ++++ .../simple_v1_0/functions.py | 80 +++++++++++++------- .../simple_v1_0/modeling/__init__.py | 2 + .../simple_v1_0/modeling/data_types.py | 6 +- .../simple_v1_0/modeling/properties.py | 17 +++-- .../simple_v1_0/templates.py | 7 +- 8 files changed, 123 insertions(+), 45 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/9de5d979/aria/modeling/service_common.py ---------------------------------------------------------------------- diff --git a/aria/modeling/service_common.py b/aria/modeling/service_common.py index 1188f34..0a7df4d 100644 --- a/aria/modeling/service_common.py +++ b/aria/modeling/service_common.py @@ -15,6 +15,8 @@ # pylint: disable=no-self-argument, no-member, abstract-method +import datetime + from sqlalchemy import ( Column, Text, @@ -22,6 +24,7 @@ from sqlalchemy import ( ) from sqlalchemy.ext.declarative import declared_attr +from ..parser import dsl_specification from ..parser.consumption import ConsumptionContext from ..utils import collections, formatting, console from .mixins import InstanceModelMixin, TemplateModelMixin @@ -90,22 +93,41 @@ class ParameterBase(TemplateModelMixin): def unwrap(self): return self.name, self.value + @dsl_specification('3.2.1-2', 'tosca-simple-1.0') @classmethod def wrap(cls, name, value, description=None): """ Wraps an arbitrary value as a parameter. The type will be guessed via introspection. + For primitive types, we will prefer their TOSCA aliases. See the `TOSCA Simple Profile v1.0 + cos01 specification <http://docs.oasis-open.org/tosca/TOSCA-Simple-Profile-YAML/v1.0/cos01 + /TOSCA-Simple-Profile-YAML-v1.0-cos01.html#_Toc373867862>`__ + :param name: Parameter name :type name: basestring :param value: Parameter value :param description: Description (optional) :type description: basestring """ - return cls(name=name, - type_name=formatting.full_type_name(value) - if value is not None else None, - value=value, - description=description) + from . import models + if value is None: + type_name = 'null' + elif isinstance(value, basestring): + type_name = 'string' + elif isinstance(value, int): + type_name = 'integer' + elif isinstance(value, float): + type_name = 'float' + elif isinstance(value, bool): + type_name = 'boolean' + elif isinstance(value, datetime.datetime): + type_name = 'timestamp' + else: + type_name = formatting.full_type_name(value) + return models.Parameter(name=name, + type_name=type_name, + value=value, + description=description) class TypeBase(InstanceModelMixin): http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/9de5d979/aria/modeling/service_instance.py ---------------------------------------------------------------------- diff --git a/aria/modeling/service_instance.py b/aria/modeling/service_instance.py index 6d8f3fe..b293535 100644 --- a/aria/modeling/service_instance.py +++ b/aria/modeling/service_instance.py @@ -515,6 +515,10 @@ class NodeBase(InstanceModelMixin): def properties(cls): return relationship.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') + @declared_attr + def attributes(cls): + return relationship.many_to_many(cls, 'parameter', prefix='attributes', dict_key='name') + # endregion description = Column(Text) @@ -649,6 +653,7 @@ class NodeBase(InstanceModelMixin): ('name', self.name), ('type_name', self.type.name), ('properties', formatting.as_raw_dict(self.properties)), + ('attributes', formatting.as_raw_dict(self.properties)), ('interfaces', formatting.as_raw_list(self.interfaces)), ('artifacts', formatting.as_raw_list(self.artifacts)), ('capabilities', formatting.as_raw_list(self.capabilities)), @@ -667,6 +672,7 @@ class NodeBase(InstanceModelMixin): # TODO: validate that node template is of type? utils.validate_dict_values(self.properties) + utils.validate_dict_values(self.attributes) utils.validate_dict_values(self.interfaces) utils.validate_dict_values(self.artifacts) utils.validate_dict_values(self.capabilities) @@ -674,6 +680,7 @@ class NodeBase(InstanceModelMixin): 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) @@ -686,6 +693,7 @@ class NodeBase(InstanceModelMixin): console.puts('Type: {0}'.format(context.style.type(self.type.name))) console.puts('Template: {0}'.format(context.style.node(self.node_template.name))) utils.dump_dict_values(self.properties, 'Properties') + utils.dump_dict_values(self.attributes, 'Attributes') utils.dump_interfaces(self.interfaces) utils.dump_dict_values(self.artifacts, 'Artifacts') utils.dump_dict_values(self.capabilities, 'Capabilities') http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/9de5d979/aria/modeling/service_template.py ---------------------------------------------------------------------- diff --git a/aria/modeling/service_template.py b/aria/modeling/service_template.py index f1c2bcb..48eeb83 100644 --- a/aria/modeling/service_template.py +++ b/aria/modeling/service_template.py @@ -503,6 +503,10 @@ class NodeTemplateBase(TemplateModelMixin): return relationship.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') @declared_attr + def attributes(cls): + return relationship.many_to_many(cls, 'parameter', prefix='attributes', dict_key='name') + + @declared_attr def interface_templates(cls): return relationship.one_to_many(cls, 'interface_template', dict_key='name') @@ -543,6 +547,7 @@ class NodeTemplateBase(TemplateModelMixin): ('min_instances', self.min_instances), ('max_instances', self.max_instances), ('properties', formatting.as_raw_dict(self.properties)), + ('attributes', formatting.as_raw_dict(self.properties)), ('interface_templates', formatting.as_raw_list(self.interface_templates)), ('artifact_templates', formatting.as_raw_list(self.artifact_templates)), ('capability_templates', formatting.as_raw_list(self.capability_templates)), @@ -559,13 +564,22 @@ class NodeTemplateBase(TemplateModelMixin): runtime_properties={}, node_template=self) utils.instantiate_dict(node, node.properties, self.properties) + utils.instantiate_dict(node, node.attributes, self.attributes) utils.instantiate_dict(node, node.interfaces, self.interface_templates) utils.instantiate_dict(node, node.artifacts, self.artifact_templates) utils.instantiate_dict(node, node.capabilities, self.capability_templates) + + # Default attributes + if 'tosca_name' in node.attributes: + node.attributes['tosca_name'].value = self.name + if 'tosca_id' in node.attributes: + node.attributes['tosca_id'].value = name + return node def validate(self): utils.validate_dict_values(self.properties) + utils.validate_dict_values(self.attributes) utils.validate_dict_values(self.interface_templates) utils.validate_dict_values(self.artifact_templates) utils.validate_dict_values(self.capability_templates) @@ -573,6 +587,7 @@ class NodeTemplateBase(TemplateModelMixin): 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) @@ -592,6 +607,7 @@ class NodeTemplateBase(TemplateModelMixin): if self.max_instances is not None else ' or more')) utils.dump_dict_values(self.properties, 'Properties') + utils.dump_dict_values(self.attributes, 'Attributes') utils.dump_interfaces(self.interface_templates) utils.dump_dict_values(self.artifact_templates, 'Artifact templates') utils.dump_dict_values(self.capability_templates, 'Capability templates') http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/9de5d979/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 405aa8f..5057e44 100644 --- a/extensions/aria_extension_tosca/simple_v1_0/functions.py +++ b/extensions/aria_extension_tosca/simple_v1_0/functions.py @@ -23,6 +23,7 @@ from aria.parser.validation import Issue from aria.modeling.exceptions import CannotEvaluateFunctionException from aria.modeling.functions import Function + # # Intrinsic # @@ -39,8 +40,8 @@ class Concat(Function): if not isinstance(argument, list): raise InvalidValueError( - 'function "concat" argument must be a list of string expressions: %s' - % safe_repr(argument), + 'function "concat" argument must be a list of string expressions: {0}' + .format(safe_repr(argument)), locator=self.locator) string_expressions = [] @@ -66,6 +67,7 @@ class Concat(Function): value.write(str(e)) return value.getvalue() + @dsl_specification('4.3.2', 'tosca-simple-1.0') class Token(Function): """ @@ -77,8 +79,8 @@ class Token(Function): self.locator = presentation._locator if (not isinstance(argument, list)) or (len(argument) != 3): - raise InvalidValueError('function "token" argument must be a list of 3 parameters: %s' - % safe_repr(argument), + raise InvalidValueError('function "token" argument must be a list of 3 parameters: {0}' + .format(safe_repr(argument)), locator=self.locator) self.string_with_tokens = parse_string_expression(context, presentation, 'token', 0, @@ -104,6 +106,7 @@ class Token(Function): if hasattr(string_with_tokens, '_evaluate'): string_with_tokens = string_with_tokens._evaluate(context, container) # pylint: disable=no-member + # # Property # @@ -127,8 +130,8 @@ class GetInput(Function): 'inputs', self.input_property_name) if the_input is None: raise InvalidValueError( - 'function "get_input" argument is not a valid input name: %s' - % safe_repr(argument), + 'function "get_input" argument is not a valid input name: {0}' + .format(safe_repr(argument)), locator=self.locator) @property @@ -143,6 +146,7 @@ class GetInput(Function): context.modeling.template.inputs.get(self.input_property_name)) return as_raw(the_input.value) if the_input is not None else None + @dsl_specification('4.4.2', 'tosca-simple-1.0') class GetProperty(Function): """ @@ -156,8 +160,7 @@ class GetProperty(Function): if (not isinstance(argument, list)) or (len(argument) < 2): raise InvalidValueError( 'function "get_property" argument must be a list of at least 2 string expressions: ' - '%s' - % safe_repr(argument), + '{0}'.format(safe_repr(argument)), locator=self.locator) self.modelable_entity_name = parse_modelable_entity_name(context, presentation, @@ -213,10 +216,11 @@ class GetProperty(Function): return as_raw(value) raise InvalidValueError( - 'function "get_property" could not find "%s" in modelable entity "%s"' \ - % ('.'.join(self.nested_property_name_or_index), self.modelable_entity_name), + 'function "get_property" could not find "{0}" in modelable entity "{1}"' + .format('.'.join(self.nested_property_name_or_index), self.modelable_entity_name), locator=self.locator) + # # Attribute # @@ -234,8 +238,7 @@ class GetAttribute(Function): if (not isinstance(argument, list)) or (len(argument) < 2): raise InvalidValueError( 'function "get_attribute" argument must be a list of at least 2 string expressions:' - ' %s' - % safe_repr(argument), + ' {0}'.format(safe_repr(argument)), locator=self.locator) self.modelable_entity_name = parse_modelable_entity_name(context, presentation, @@ -250,6 +253,7 @@ class GetAttribute(Function): def _evaluate(self, context, container): # pylint: disable=no-self-use,unused-argument raise CannotEvaluateFunctionException() + # # Operation # @@ -266,8 +270,8 @@ class GetOperationOutput(Function): if (not isinstance(argument, list)) or (len(argument) != 4): raise InvalidValueError( - 'function "get_operation_output" argument must be a list of 4 parameters: %s' - % safe_repr(argument), + 'function "get_operation_output" argument must be a list of 4 parameters: {0}' + .format(safe_repr(argument)), locator=self.locator) self.modelable_entity_name = parse_string_expression(context, presentation, @@ -295,6 +299,7 @@ class GetOperationOutput(Function): return {'get_operation_output': [self.modelable_entity_name, interface_name, operation_name, output_variable_name]} + # # Navigation # @@ -316,8 +321,8 @@ class GetNodesOfType(Function): node_types = context.presentation.get('service_template', 'node_types') if (node_types is None) or (self.node_type_name not in node_types): raise InvalidValueError( - 'function "get_nodes_of_type" argument is not a valid node type name: %s' - % safe_repr(argument), + 'function "get_nodes_of_type" argument is not a valid node type name: {0}' + .format(safe_repr(argument)), locator=self.locator) @property @@ -330,6 +335,7 @@ class GetNodesOfType(Function): def _evaluate(self, context, container): pass + # # Artifact # @@ -346,8 +352,8 @@ class GetArtifact(Function): if (not isinstance(argument, list)) or (len(argument) < 2) or (len(argument) > 4): raise InvalidValueError( - 'function "get_artifact" argument must be a list of 2 to 4 parameters: %s' - % safe_repr(argument), + 'function "get_artifact" argument must be a list of 2 to 4 parameters: {0}' + .format(safe_repr(argument)), locator=self.locator) self.modelable_entity_name = parse_string_expression(context, presentation, 'get_artifact', @@ -370,6 +376,7 @@ class GetArtifact(Function): location = as_raw(location) return {'get_artifacts': [self.modelable_entity_name, artifact_name, location, self.remove]} + # # Utils # @@ -386,6 +393,7 @@ def get_function(context, presentation, value): return True, None return False, None + def parse_string_expression(context, presentation, name, index, explanation, value): # pylint: disable=unused-argument is_function, func = get_function(context, presentation, value) if is_function: @@ -394,6 +402,7 @@ def parse_string_expression(context, presentation, name, index, explanation, val value = str(value) return value + def parse_int(context, presentation, name, index, explanation, value): # pylint: disable=unused-argument if not isinstance(value, int): try: @@ -403,11 +412,13 @@ def parse_int(context, presentation, name, index, explanation, value): # pylint: presentation._locator) return value + def parse_bool(context, presentation, name, index, explanation, value): # pylint: disable=unused-argument if not isinstance(value, bool): raise invalid_value(name, index, 'a boolean', explanation, value, presentation._locator) return value + def parse_modelable_entity_name(context, presentation, name, index, value): value = parse_string_expression(context, presentation, name, index, 'the modelable entity name', value) @@ -436,11 +447,12 @@ def parse_modelable_entity_name(context, presentation, name, index, value): or {} if (value not in node_templates) and (value not in relationship_templates): raise InvalidValueError( - 'function "%s" parameter %d is not a valid modelable entity name: %s' - % (name, index + 1, safe_repr(value)), + 'function "{0}" parameter {1:d} is not a valid modelable entity name: {2}' + .format(name, index + 1, safe_repr(value)), locator=presentation._locator, level=Issue.BETWEEN_TYPES) return value + def parse_self(presentation): from .templates import NodeTemplate, RelationshipTemplate from .types import NodeType, RelationshipType @@ -455,6 +467,7 @@ def parse_self(presentation): else: return parse_self(presentation._container) + @dsl_specification('4.1', 'tosca-simple-1.0') def get_modelable_entities(context, container, locator, modelable_entity_name): """ @@ -465,7 +478,7 @@ def get_modelable_entities(context, container, locator, modelable_entity_name): if modelable_entity_name == 'SELF': return get_self(context, container) elif modelable_entity_name == 'HOST': - return get_host(context, container) + return get_hosts(context, container) elif modelable_entity_name == 'SOURCE': return get_source(context, container) elif modelable_entity_name == 'TARGET': @@ -483,10 +496,11 @@ def get_modelable_entities(context, container, locator, modelable_entity_name): if modelable_entity_name in relationship_templates: return [relationship_templates[modelable_entity_name]] - raise InvalidValueError('function "get_property" could not find modelable entity "%s"' - % modelable_entity_name, + raise InvalidValueError('function "get_property" could not find modelable entity "{0}"' + .format(modelable_entity_name), locator=locator) + def get_self(context, container): # pylint: disable=unused-argument """ A TOSCA orchestrator will interpret this keyword as the Node or Relationship Template instance @@ -495,7 +509,8 @@ def get_self(context, container): # pylint: disable=unused-argument return [container] -def get_host(context, container): # pylint: disable=unused-argument + +def get_hosts(context, container): # 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). @@ -509,6 +524,7 @@ def get_host(context, container): # pylint: disable=unused-argument return [] + def get_source(context, container): # pylint: disable=unused-argument """ A TOSCA orchestrator will interpret this keyword as the Node Template instance that is at the @@ -517,20 +533,26 @@ def get_source(context, container): # pylint: disable=unused-argument return [] + def get_target(context, container): # 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 invalid_modelable_entity_name(name, index, value, locator, contexts): - return InvalidValueError('function "%s" parameter %d can be "%s" only in %s' - % (name, index + 1, value, contexts), + return InvalidValueError('function "{0}" parameter {1:d} can be "{2}" only in {3}' + .format(name, index + 1, value, contexts), locator=locator, level=Issue.FIELD) + def invalid_value(name, index, the_type, explanation, value, locator): return InvalidValueError( - 'function "%s" %s is not %s%s: %s' - % (name, ('parameter %d' % (index + 1)) if index is not None else 'argument', - the_type, (', %s' % explanation) if explanation is not None else '', safe_repr(value)), + 'function "{0}" {1} is not {2}{3}: {4}' + .format(name, + 'parameter {0:d}'.format(index + 1) if index is not None else 'argument', + the_type, + ', {0}'.format(explanation) if explanation is not None else '', + safe_repr(value)), locator=locator, level=Issue.FIELD) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/9de5d979/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 9576260..945f795 100644 --- a/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py +++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py @@ -164,6 +164,8 @@ def create_node_template_model(context, service_template, node_template): create_parameter_models_from_values(model.properties, node_template._get_property_values(context)) + create_parameter_models_from_values(model.attributes, + node_template._get_attribute_default_values(context)) create_interface_template_models(context, service_template, model.interface_templates, node_template._get_interfaces(context)) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/9de5d979/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 99dcfea..6c7b780 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 @@ -327,20 +327,20 @@ def get_data_type_value(context, presentation, field_name, type_name): PRIMITIVE_DATA_TYPES = { # YAML 1.2: - 'tag:yaml.org,2002:str': str, + 'tag:yaml.org,2002:str': unicode, 'tag:yaml.org,2002:integer': int, 'tag:yaml.org,2002:float': float, 'tag:yaml.org,2002:bool': bool, 'tag:yaml.org,2002:null': None.__class__, # TOSCA aliases: - 'string': str, + 'string': unicode, 'integer': int, 'float': float, 'boolean': bool, 'null': None.__class__} -@dsl_specification('3.2.1', 'tosca-simple-1.0') +@dsl_specification('3.2.1-1', 'tosca-simple-1.0') def get_primitive_data_type(type_name): """ Many of the types we use in this profile are built-in types from the YAML 1.2 specification http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/9de5d979/extensions/aria_extension_tosca/simple_v1_0/modeling/properties.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/properties.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/properties.py index f61cb99..9c3ea42 100644 --- a/extensions/aria_extension_tosca/simple_v1_0/modeling/properties.py +++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/properties.py @@ -58,7 +58,8 @@ def get_inherited_property_definitions(context, presentation, field_name, for_pr # NodeTemplate, RelationshipTemplate, GroupTemplate, PolicyTemplate # -def get_assigned_and_defined_property_values(context, presentation): +def get_assigned_and_defined_property_values(context, presentation, field_name='property', + field_name_plural='properties'): """ Returns the assigned property values while making sure they are defined in our type. @@ -70,8 +71,9 @@ def get_assigned_and_defined_property_values(context, presentation): values = OrderedDict() the_type = presentation._get_type(context) - assignments = presentation.properties - definitions = the_type._get_properties(context) if the_type is not None else None + assignments = getattr(presentation, field_name_plural) + get_fn_name = '_get_{0}'.format(field_name_plural) + definitions = getattr(the_type, get_fn_name)(context) if the_type is not None else None # Fill in our assignments, but make sure they are defined if assignments: @@ -80,14 +82,14 @@ def get_assigned_and_defined_property_values(context, presentation): definition = definitions[name] values[name] = coerce_property_value(context, value, definition, value.value) else: - context.validation.report('assignment to undefined property "%s" in "%s"' - % (name, presentation._fullname), + context.validation.report('assignment to undefined {0} "{1}" in "{2}"' + .format(field_name, name, presentation._fullname), locator=value._locator, level=Issue.BETWEEN_TYPES) # Fill in defaults from the definitions if definitions: for name, definition in definitions.iteritems(): - if (values.get(name) is None) and (definition.default is not None): + if values.get(name) is None: values[name] = coerce_property_value(context, presentation, definition, definition.default) @@ -181,7 +183,8 @@ def merge_property_definitions(context, presentation, property_definitions, def coerce_property_value(context, presentation, definition, value, aspect=None): the_type = definition._get_type(context) if definition is not None else None entry_schema = definition.entry_schema if definition is not None else None - constraints = definition._get_constraints(context) if definition is not None else None + constraints = definition._get_constraints(context) \ + if ((definition is not None) and hasattr(definition, '_get_constraints')) else None value = coerce_value(context, presentation, the_type, entry_schema, constraints, value, aspect) if (the_type is not None) and hasattr(the_type, '_name'): type_name = the_type._name http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/9de5d979/extensions/aria_extension_tosca/simple_v1_0/templates.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/templates.py b/extensions/aria_extension_tosca/simple_v1_0/templates.py index 6860b72..c0f9f23 100644 --- a/extensions/aria_extension_tosca/simple_v1_0/templates.py +++ b/extensions/aria_extension_tosca/simple_v1_0/templates.py @@ -26,7 +26,7 @@ from .assignments import (PropertyAssignment, AttributeAssignment, RequirementAs from .definitions import ParameterDefinition from .filters import NodeFilter from .misc import (Description, MetaData, Repository, Import, SubstitutionMappings) -from .modeling.properties import get_assigned_and_defined_property_values, get_parameter_values +from .modeling.properties import (get_assigned_and_defined_property_values, get_parameter_values) from .modeling.interfaces import get_template_interfaces from .modeling.requirements import get_template_requirements from .modeling.capabilities import get_template_capabilities @@ -160,6 +160,11 @@ class NodeTemplate(ExtensiblePresentation): return FrozenDict(get_assigned_and_defined_property_values(context, self)) @cachedmethod + def _get_attribute_default_values(self, context): + return FrozenDict(get_assigned_and_defined_property_values(context, self, + 'attribute', 'attributes')) + + @cachedmethod def _get_requirements(self, context): return FrozenList(get_template_requirements(context, self))
