Fixed incorrect information on Python frames in MuranoPL stack traces

Python frames in mixed stack traces were missing file name and pointed to a line
below correct position

Change-Id: I335292f40b3b6ea3dbca80b84f1d8dbed9a6581d
Fixes: bug #1331113
This commit is contained in:
Stan Lagun
2014-06-17 22:26:57 +04:00
parent a6d377d992
commit 705a0f5838
5 changed files with 58 additions and 24 deletions

View File

@@ -123,7 +123,7 @@ class TaskExecutor(object):
self._invoke(exc) self._invoke(exc)
except Exception as e: except Exception as e:
if isinstance(e, dsl_exception.MuranoPlException): if isinstance(e, dsl_exception.MuranoPlException):
LOG.error(e.format()) LOG.error('\n' + e.format(prefix=' '))
else: else:
LOG.exception(e) LOG.exception(e)
reporter = status_reporter.StatusReporter() reporter = status_reporter.StatusReporter()

View File

@@ -71,8 +71,8 @@ class MuranoPlException(Exception):
else: else:
return self._names return self._names
def format(self, prefix=' '): def format(self, prefix=''):
text = '\n{3}{0}: {1}\n' \ text = '{3}{0}: {1}\n' \
'{3}Traceback (most recent call last):\n' \ '{3}Traceback (most recent call last):\n' \
'{2}'.format(self._format_name(), self.message, '{2}'.format(self._format_name(), self.message,
self.stacktrace.toString(prefix + ' '), prefix) self.stacktrace.toString(prefix + ' '), prefix)

View File

@@ -45,25 +45,15 @@ class StackTrace(murano_object.MuranoObject):
frames.reverse() frames.reverse()
if includeNativeFrames: if includeNativeFrames:
class InstructionStub(object):
def __init__(self, title, position):
self._title = title
self.source_file_position = position
def __str__(self):
return self._title
native_frames = [] native_frames = []
for frame in inspect.trace()[1:]: for frame in inspect.trace()[1:]:
info = inspect.getframeinfo(frame[0]) location = yaql_expression.YaqlExpressionFilePosition(
position = yaql_expression.YaqlExpressionFilePosition( os.path.abspath(frame[1]), frame[2],
os.path.abspath(info.filename), info.lineno,
-1, -1, -1, -1, -1) -1, -1, -1, -1, -1)
instruction = InstructionStub( method = frame[3]
info.code_context[0].strip(), position)
method = info.function
native_frames.append({ native_frames.append({
'instruction': instruction, 'instruction': frame[4][0].strip(),
'location': location,
'method': method, 'method': method,
'class': None 'class': None
}) })
@@ -77,6 +67,8 @@ class StackTrace(murano_object.MuranoObject):
method = frame['method'] method = frame['method']
murano_class = frame['class'] murano_class = frame['class']
location = frame['location'] location = frame['location']
if murano_class:
method += ' of class ' + murano_class.name
if location: if location:
args = ( args = (
@@ -86,14 +78,12 @@ class StackTrace(murano_object.MuranoObject):
if location.start_column >= 0 else '', if location.start_column >= 0 else '',
method, method,
instruction, instruction,
prefix, prefix
'' if not murano_class else murano_class.name + '::'
) )
return '{5}File "{0}", line {1}{2} in method {6}{3}\n' \ return '{5}File "{0}", line {1}{2} in method {3}\n' \
'{5} {4}'.format(*args) '{5} {4}'.format(*args)
else: else:
return '{2}File <unknown> in method {3}{0}\n{2} {1}'.format( return '{2}File <unknown> in method {0}\n{2} {1}'.format(
method, instruction, prefix, method, instruction, prefix)
'' if not murano_class else murano_class.name + '::')
return '\n'.join([format_frame(t)for t in self.get_property('frames')]) return '\n'.join([format_frame(t)for t in self.get_property('frames')])

View File

@@ -49,3 +49,7 @@ Methods:
- Return: - Return:
Value: $enum Value: $enum
testStackTrace:
Body:
raisePythonException()

View File

@@ -12,6 +12,12 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import inspect
import os.path
import re
from testtools import matchers
from murano.dsl import dsl_exception from murano.dsl import dsl_exception
from murano.tests.dsl.foundation import object_model as om from murano.tests.dsl.foundation import object_model as om
from murano.tests.dsl.foundation import test_case from murano.tests.dsl.foundation import test_case
@@ -20,6 +26,16 @@ from murano.tests.dsl.foundation import test_case
class TestExceptions(test_case.DslTestCase): class TestExceptions(test_case.DslTestCase):
def setUp(self): def setUp(self):
super(TestExceptions, self).setUp() super(TestExceptions, self).setUp()
def exception_func():
exc = LookupError('just random Python exception')
frameinfo = inspect.getframeinfo(inspect.currentframe())
exc._position = \
os.path.basename(frameinfo.filename), frameinfo.lineno + 4
# line below must be exactly 4 lines after currentframe()
raise exc
self.register_function(exception_func, 'raisePythonException')
self._runner = self.new_runner(om.Object('ExceptionHandling')) self._runner = self.new_runner(om.Object('ExceptionHandling'))
def test_throw_catch(self): def test_throw_catch(self):
@@ -54,3 +70,27 @@ class TestExceptions(test_case.DslTestCase):
self.assertEqual( self.assertEqual(
['enter try', 'exit try', 'else section', 'finally section'], ['enter try', 'exit try', 'else section', 'finally section'],
self.traces) self.traces)
def test_stack_trace(self):
self._runner.preserve_exception = True
e = self.assertRaises(
dsl_exception.MuranoPlException,
self._runner.testStackTrace)
call_stack = e.format()
self.assertThat(
call_stack,
matchers.StartsWith(
'exceptions.LookupError: just random Python exception'))
self.assertIsInstance(e.original_exception, LookupError)
filename, line = e.original_exception._position
self.assertThat(
call_stack,
matchers.MatchesRegex(
r'.*^ File \".*ExceptionHandling\.yaml\", '
r'line \d+:\d+ in method testStackTrace .*'
r'of class ExceptionHandling$.*'
r'^ File \".*{0}\", line {1} '
r'in method exception_func$.*'.format(filename, line),
re.MULTILINE | re.DOTALL))