deb-murano/murano/dsl/lhs_expression.py
Stan Lagun 8a7b2cef50 LHS assignment expressions didn't work with non-trivial indexation
Most of assignment expressions involving indexation operator except
for the most trivial one fail.

For example the following failed:

$myDict[$keyName]: value
$myDict['A']['B']: value (even if $myDict['A'] is a dictionary)
$myList[2 + 2]: value
$myList[$index]: value

This commit adds support for sub-expressions within LHS-expressions

Change-Id: I4b72e85b5f9bda04c6156d5c8b07eb8f5358643d
Closes-Bug: #1358545
2014-08-19 12:49:30 +04:00

145 lines
5.5 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 _evaluate(thing):
if isinstance(thing, yaql.expressions.Expression.Callable):
thing.yaql_context = root_context
thing = thing()
return thing
def _get_value(src, key):
key = _evaluate(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):
key = _evaluate(key)
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):
index = _evaluate(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)