deb-murano/murano/dsl/lhs_expression.py
Stan Lagun 65783894f1 Unit tests for method and property access
Tests for method invocation in property access in different
inheritance topologies (single inheritance, multiple inheritance,
 diamond inheritance)

Also fixes invalid property resolution algorithm that failed for methods
that could be found on several inheritance paths (diamond scheme)

Change-Id: I68ceae8fcab839b230d2acef667982897d554011
Closes-Bug: #1337458
Partial-Bug: #1316786
2014-07-03 22:05:04 +04:00

135 lines
5.2 KiB
Python

# Copyright (c) 2014 Mirantis, Inc.
#
# Licensed 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.
import types
import yaql
import yaql.context
import yaql.expressions
import murano.dsl.murano_object as murano_object
import murano.dsl.type_scheme as type_scheme
import murano.dsl.yaql_expression as yaql_expression
class LhsExpression(object):
class Property(object):
def __init__(self, getter, setter):
self._getter = getter
self._setter = setter
def get(self):
return self._getter()
def set(self, value):
self._setter(value)
def __init__(self, expression):
if isinstance(expression, (yaql_expression.YaqlExpression,
yaql.expressions.Expression)):
self._expression = expression
else:
self._expression = yaql.parse(str(expression))
self._current_obj = None
self._current_obj_name = None
def _create_context(self, root_context, murano_class):
def _get_value(src, key):
if isinstance(src, types.DictionaryType):
return src.get(key)
elif isinstance(src, types.ListType) and isinstance(
key, types.IntType):
return src[key]
elif isinstance(src, murano_object.MuranoObject) and isinstance(
key, types.StringTypes):
self._current_obj = src
self._current_obj_name = key
return src.get_property(key, murano_class)
else:
raise TypeError()
def _set_value(src, key, value):
if isinstance(src, types.DictionaryType):
old_value = src.get(key, type_scheme.NoValue)
src[key] = value
if self._current_obj is not None:
try:
p_value = self._current_obj.get_property(
self._current_obj_name, murano_class)
self._current_obj.set_property(
self._current_obj_name, p_value, murano_class)
except Exception as e:
if old_value is not type_scheme.NoValue:
src[key] = old_value
else:
src.pop(key, None)
raise e
elif isinstance(src, types.ListType) and isinstance(
key, types.IntType):
old_value = src[key]
src[key] = value
if self._current_obj is not None:
try:
p_value = self._current_obj.get_property(
self._current_obj_name, murano_class)
self._current_obj.set_property(
self._current_obj_name, p_value, murano_class)
except Exception as e:
src[key] = old_value
raise e
elif isinstance(src, murano_object.MuranoObject) and isinstance(
key, types.StringTypes):
src.set_property(key, value, murano_class)
else:
raise TypeError()
def get_context_data(path):
path = path()
def set_data(value):
if not path or path == '$' or path == '$this':
raise ValueError()
root_context.set_data(value, path)
return LhsExpression.Property(
lambda: root_context.get_data(path), set_data)
@yaql.context.EvalArg('this', arg_type=LhsExpression.Property)
def attribution(this, arg_name):
arg_name = arg_name()
return LhsExpression.Property(
lambda: _get_value(this.get(), arg_name),
lambda value: _set_value(this.get(), arg_name, value))
@yaql.context.EvalArg("this", LhsExpression.Property)
def indexation(this, index):
return LhsExpression.Property(
lambda: _get_value(this.get(), index()),
lambda value: _set_value(this.get(), index(), value))
context = yaql.context.Context()
context.register_function(get_context_data, '#get_context_data')
context.register_function(attribution, '#operator_.')
context.register_function(indexation, "where")
return context
def __call__(self, value, context, murano_class):
new_context = self._create_context(context, murano_class)
new_context.set_data(context.get_data('$'))
self._current_obj = None
self._current_obj_name = None
property = self._expression.evaluate(context=new_context)
property.set(value)