Merge "Maintain virtual MuranoPL stack trace"

This commit is contained in:
Jenkins
2014-06-11 23:28:25 +00:00
committed by Gerrit Code Review
8 changed files with 184 additions and 15 deletions

View File

@@ -35,7 +35,7 @@ def serialize(value, memo=None):
result[d_key] = serialize(d_value, memo)
return result
elif isinstance(value, murano.dsl.murano_object.MuranoObject):
if not value.object_id in memo:
if value.object_id not in memo:
memo.add(value.object_id)
return serialize(value.to_dictionary(), memo)
else:
@@ -47,13 +47,19 @@ def serialize(value, memo=None):
def evaluate(value, context, max_depth=sys.maxint):
if isinstance(value, (yaql_expression.YaqlExpression,
yaql.expressions.Expression)):
if isinstance(value, yaql.expressions.Expression):
value = yaql_expression.YaqlExpression(value)
if isinstance(value, yaql_expression.YaqlExpression):
func = lambda: evaluate(value.evaluate(context), context, 1)
if max_depth <= 0:
return func
else:
return func()
try:
context.set_data(value, '?currentInstruction')
return func()
finally:
context.set_data(None, '?currentInstruction')
elif isinstance(value, types.DictionaryType):
result = {}
@@ -178,3 +184,11 @@ def get_caller_context(context):
def get_attribute_store(context):
return context.get_data('$?attributeStore')
def get_current_instruction(context):
return context.get_data('$?currentInstruction')
def get_current_method(context):
return context.get_data('$?currentMethod')

View File

@@ -42,14 +42,21 @@ class CodeBlock(expressions.DslExpression):
class MethodBlock(CodeBlock):
def __init__(self, body, name=None):
super(MethodBlock, self).__init__(body)
self._name = name
def execute(self, context, murano_class):
try:
context.set_data(self._name, '?currentMethod')
super(MethodBlock, self).execute(
context, murano_class)
except exceptions.ReturnException as e:
return e.value
else:
return None
finally:
context.set_data(None, '?currentMethod')
class ReturnMacro(expressions.DslExpression):

View File

@@ -101,6 +101,15 @@ class MuranoClass(object):
if initial and not yielded:
yield initial
def find_method(self, name):
if name in self._methods:
return [(self, name)]
if not self._parents:
return []
return list(set(reduce(
lambda x, y: x + y,
[p.find_method(name) for p in self._parents])))
def find_single_method(self, name):
chains = sorted(self._find_method_chains(name), key=lambda t: len(t))
result = []

View File

@@ -51,7 +51,7 @@ class MuranoMethod(object):
MethodUsages.Runtime)
else:
payload = payload or {}
self._body = self._prepare_body(payload.get('Body') or [])
self._body = self._prepare_body(payload.get('Body') or [], name)
self._usage = payload.get('Usage') or MethodUsages.Runtime
arguments_scheme = payload.get('Arguments') or []
if isinstance(arguments_scheme, types.DictionaryType):
@@ -105,8 +105,8 @@ class MuranoMethod(object):
del result['_context']
return result
def _prepare_body(self, body):
return macros.MethodBlock(body)
def _prepare_body(self, body, name):
return macros.MethodBlock(body, name)
def __repr__(self):
return 'MuranoMethod({0}::{1})'.format(

View File

@@ -0,0 +1,66 @@
# 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 os.path
import yaql.context
import murano.dsl.helpers as helpers
@yaql.context.ContextAware()
def stack_trace(context):
frames = []
while True:
context = helpers.get_caller_context(context)
if not context:
break
instruction = helpers.get_current_instruction(context)
frames.append({
'instruction': None if instruction is None else str(instruction),
'location': None if instruction is None
else instruction.file_position,
'method': helpers.get_current_method(context),
'class': helpers.get_type(context)
})
frames.pop()
return frames
def format_stack_trace_yaql(trace):
return format_stack_trace(trace, '')
def format_stack_trace(trace, prefix=''):
def format_frame(frame):
instruction = frame['instruction']
method = frame['method']
murano_class = frame['class']
location = frame['location']
if location:
return '{5}File "{0}" at {1}:{2} in method {3} of class {6}\n' \
'{5} {4}'.format(
os.path.abspath(location.file_path),
location.start_line,
location.start_column,
method,
instruction,
prefix,
murano_class.name)
else:
return '{2}File <unknown> in method {0}\n{2} {1}'.format(
method, instruction, prefix)
return '\n'.join([format_frame(t)for t in trace()])

View File

@@ -17,16 +17,38 @@ import types
import yaql
import yaql.exceptions
import yaql.expressions
class YaqlExpression(object):
def __init__(self, expression):
self._expression = str(expression)
self._parsed_expression = yaql.parse(self._expression)
if isinstance(expression, types.StringTypes):
self._expression = str(expression)
self._parsed_expression = yaql.parse(self._expression)
self._file_position = None
elif isinstance(expression, YaqlExpression):
self._expression = expression._expression
self._parsed_expression = expression._parsed_expression
self._file_position = expression._file_position
elif isinstance(expression, yaql.expressions.Expression):
self._expression = str(expression)
self._parsed_expression = expression
self._file_position = None
else:
raise TypeError('expression is not of supported types')
@property
def expression(self):
return self._expression
@property
def file_position(self):
return self._file_position
@file_position.setter
def file_position(self, value):
self._file_position = value
def __repr__(self):
return 'YAQL(%s)' % self._expression
@@ -49,3 +71,43 @@ class YaqlExpression(object):
def evaluate(self, context=None):
return self._parsed_expression.evaluate(context=context)
class YaqlExpressionFilePosition(object):
def __init__(self, file_path, start_line, start_column, start_index,
end_line, end_column, length):
self._file_path = file_path
self._start_line = start_line
self._start_column = start_column
self._start_index = start_index
self._end_line = end_line
self._end_column = end_column
self._length = length
@property
def file_path(self):
return self._file_path
@property
def start_line(self):
return self._start_line
@property
def start_column(self):
return self._start_column
@property
def start_index(self):
return self._start_index
@property
def end_line(self):
return self._end_line
@property
def end_column(self):
return self._end_column
@property
def length(self):
return self._length

View File

@@ -30,7 +30,17 @@ YaqlYamlLoader.yaml_implicit_resolvers = resolvers
def yaql_constructor(loader, node):
value = loader.construct_scalar(node)
return yaql_expression.YaqlExpression(value)
result = yaql_expression.YaqlExpression(value)
position = yaql_expression.YaqlExpressionFilePosition(
node.start_mark.name,
node.start_mark.line + 1,
node.start_mark.column + 1,
node.start_mark.index,
node.end_mark.line + 1,
node.end_mark.column + 1,
node.end_mark.index - node.start_mark.index)
result.file_position = position
return result
yaml.add_constructor(u'!yaql', yaql_constructor, YaqlYamlLoader)
yaml.add_implicit_resolver(u'!yaql', yaql_expression.YaqlExpression,

View File

@@ -453,11 +453,12 @@ class TestHelperFunctions(base.MuranoTestCase):
# tuple(evaluate(list)) transformation adds + 1
complex_literal_depth = 3 + 1
evaluated_value = helpers.evaluate(yaql_value, None, 1)
non_evaluated_value = helpers.evaluate(yaql_value, None, 0)
evaluated_complex_value = helpers.evaluate(complex_value, None)
context = yaql.create_context(False)
evaluated_value = helpers.evaluate(yaql_value, context, 1)
non_evaluated_value = helpers.evaluate(yaql_value, context, 0)
evaluated_complex_value = helpers.evaluate(complex_value, context)
non_evaluated_complex_value = helpers.evaluate(
complex_value, None, complex_literal_depth)
complex_value, context, complex_literal_depth)
self.assertEqual('atom', evaluated_value)
self.assertNotEqual('atom', non_evaluated_value)
@@ -484,7 +485,7 @@ class TestYaqlExpression(base.MuranoTestCase):
def test_expression(self):
yaql_expr = yaql_expression.YaqlExpression('string')
self.assertEqual('string', yaql_expr.expression())
self.assertEqual('string', yaql_expr.expression)
def test_evaluate_calls(self):
string = 'string'