diff options
Diffstat (limited to 'azure/aria/aria-extension-cloudify/src/aria/aria/modeling/utils.py')
-rw-r--r-- | azure/aria/aria-extension-cloudify/src/aria/aria/modeling/utils.py | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/azure/aria/aria-extension-cloudify/src/aria/aria/modeling/utils.py b/azure/aria/aria-extension-cloudify/src/aria/aria/modeling/utils.py new file mode 100644 index 0000000..491b71a --- /dev/null +++ b/azure/aria/aria-extension-cloudify/src/aria/aria/modeling/utils.py @@ -0,0 +1,185 @@ +# 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. + +""" +Miscellaneous modeling utilities. +""" + +import os +from json import JSONEncoder +from StringIO import StringIO + +from . import exceptions +from ..utils.type import validate_value_type +from ..utils.collections import OrderedDict +from ..utils.formatting import string_list_as_string + + +class ModelJSONEncoder(JSONEncoder): + """ + JSON encoder that automatically unwraps ``value`` attributes. + """ + def __init__(self, *args, **kwargs): + # Just here to make sure Sphinx doesn't grab the base constructor's docstring + super(ModelJSONEncoder, self).__init__(*args, **kwargs) + + def default(self, o): # pylint: disable=method-hidden + from .mixins import ModelMixin + if isinstance(o, ModelMixin): + if hasattr(o, 'value'): + dict_to_return = o.to_dict(fields=('value',)) + return dict_to_return['value'] + else: + return o.to_dict() + else: + return JSONEncoder.default(self, o) + + +class NodeTemplateContainerHolder(object): + """ + Wrapper that allows using a :class:`~aria.modeling.models.NodeTemplate` model directly as the + ``container_holder`` input for :func:`~aria.modeling.functions.evaluate`. + """ + + def __init__(self, node_template): + self.container = node_template + self.service = None + + @property + def service_template(self): + return self.container.service_template + + +# def validate_no_undeclared_inputs(declared_inputs, supplied_inputs): +# +# undeclared_inputs = [input for input in supplied_inputs if input not in declared_inputs] +# if undeclared_inputs: +# raise exceptions.UndeclaredInputsException( +# 'Undeclared inputs have been provided: {0}; Declared inputs: {1}' +# .format(string_list_as_string(undeclared_inputs), +# string_list_as_string(declared_inputs.keys()))) + + +def validate_required_inputs_are_supplied(declared_inputs, supplied_inputs): + required_inputs = [input for input in declared_inputs.values() if input.required] + missing_required_inputs = [input for input in required_inputs + if input.name not in supplied_inputs and not str(input.value)] + if missing_required_inputs: + raise exceptions.MissingRequiredInputsException( + 'Required inputs {0} have not been provided values' + .format(string_list_as_string(missing_required_inputs))) + + +def merge_parameter_values(provided_values, declared_parameters, model_cls=None): + """ + Merges parameter values according to those declared by a type. + + Exceptions will be raised for validation errors. + + :param provided_values: provided parameter values or None + :type provided_values: {:obj:`basestring`: object} + :param declared_parameters: declared parameters + :type declared_parameters: {:obj:`basestring`: :class:`~aria.modeling.models.Parameter`} + :param model_cls: the model class that should be created from a provided value + :type model_cls: :class:`~aria.modeling.models.Input` or :class:`~aria.modeling.models.Argument` + :return: the merged parameters + :rtype: {:obj:`basestring`: :class:`~aria.modeling.models.Parameter`} + :raises ~aria.modeling.exceptions.UndeclaredInputsException: if a key in + ``parameter_values`` does not exist in ``declared_parameters`` + :raises ~aria.modeling.exceptions.MissingRequiredInputsException: if a key in + ``declared_parameters`` does not exist in ``parameter_values`` and also has no default value + :raises ~aria.modeling.exceptions.ParametersOfWrongTypeException: if a value in + ``parameter_values`` does not match its type in ``declared_parameters`` + """ + + provided_values = provided_values or {} + provided_values_of_wrong_type = OrderedDict() + model_parameters = OrderedDict() + model_cls = model_cls or _get_class_from_sql_relationship(declared_parameters) + + for declared_parameter_name, declared_parameter in declared_parameters.iteritems(): + if declared_parameter_name in provided_values: + # a value has been provided + value = provided_values[declared_parameter_name] + + # Validate type + type_name = declared_parameter.type_name + try: + validate_value_type(value, type_name) + except ValueError: + provided_values_of_wrong_type[declared_parameter_name] = type_name + except RuntimeError: + # TODO This error shouldn't be raised (or caught), but right now we lack support + # for custom data_types, which will raise this error. Skipping their validation. + pass + model_parameters[declared_parameter_name] = model_cls( # pylint: disable=unexpected-keyword-arg + name=declared_parameter_name, + type_name=type_name, + description=declared_parameter.description, + value=value) + else: + # Copy default value from declaration + model_parameters[declared_parameter_name] = model_cls( + value=declared_parameter._value, + name=declared_parameter.name, + type_name=declared_parameter.type_name, + description=declared_parameter.description) + + if provided_values_of_wrong_type: + error_message = StringIO() + for param_name, param_type in provided_values_of_wrong_type.iteritems(): + error_message.write('Parameter "{0}" is not of declared type "{1}"{2}' + .format(param_name, param_type, os.linesep)) + raise exceptions.ParametersOfWrongTypeException(error_message.getvalue()) + + return model_parameters + + +def parameters_as_values(the_dict): + return dict((k, v.value) for k, v in the_dict.iteritems()) + + +def dict_as_arguments(the_dict): + return OrderedDict((name, value.as_argument()) for name, value in the_dict.iteritems()) + + +class classproperty(object): # pylint: disable=invalid-name + def __init__(self, f): + self._func = f + self.__doct__ = f.__doc__ + + def __get__(self, instance, owner): + return self._func(owner) + + +def fix_doc(cls): + """ + Class decorator to use the last base class's docstring and make sure Sphinx doesn't grab the + base constructor's docstring. + """ + original_init = cls.__init__ + def init(*args, **kwargs): + original_init(*args, **kwargs) + + cls.__init__ = init + cls.__doc__ = cls.__bases__[-1].__doc__ + + return cls + + +def _get_class_from_sql_relationship(field): + class_ = field._sa_adapter.owner_state.class_ + prop_name = field._sa_adapter.attr.key + return getattr(class_, prop_name).property.mapper.class_ |