Adding v2 error line support, and tests

This commit is contained in:
James Cammarata 2014-10-09 12:52:09 -05:00
parent 383a44a462
commit a50332fc8a
4 changed files with 91 additions and 6 deletions

View file

@ -0,0 +1,18 @@
# (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.

View file

@ -0,0 +1,33 @@
# TODO: header
import unittest
from mock import mock_open, patch
from ansible.parsing.yaml.objects import AnsibleBaseYAMLObject
from ansible.errors import AnsibleError
class TestErrors(unittest.TestCase):
def setUp(self):
self.message = 'this is the error message'
def tearDown(self):
pass
def test_basic_error(self):
e = AnsibleError(self.message)
assert e.message == self.message
def test_error_with_object(self):
obj = AnsibleBaseYAMLObject()
obj._data_source = 'foo.yml'
obj._line_number = 1
obj._column_number = 1
m = mock_open()
m.return_value.readlines.return_value = ['this is line 1\n', 'this is line 2\n', 'this is line 3\n']
with patch('__builtin__.open', m):
e = AnsibleError(self.message, obj)
assert e.message == 'this is the error message\nThe error occurred on line 1 of the file foo.yml:\nthis is line 1\n^'

View file

@ -15,15 +15,46 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>. # along with Ansible. If not, see <http://www.gnu.org/licenses/>.
import os
from ansible.parsing.yaml.objects import AnsibleBaseYAMLObject
class AnsibleError(Exception): class AnsibleError(Exception):
def __init__(self, message, object=None): def __init__(self, message, obj=None):
self._obj = obj
if isinstance(self._obj, AnsibleBaseYAMLObject):
extended_error = self._get_extended_error()
if extended_error:
self.message = '%s\n%s' % (message, extended_error)
else:
self.message = message self.message = message
self.object = object
# TODO: nice __repr__ message that includes the line number if the object def __repr__(self):
# it was constructed with had the line number return self.message
# TODO: tests for the line number functionality def _get_line_from_file(self, filename, line_number):
with open(filename, 'r') as f:
lines = f.readlines()
if line_number < len(lines):
return lines[line_number]
return None
def _get_extended_error(self):
error_message = ''
try:
(src_file, line_number, col_number) = self._obj.get_position_info()
error_message += 'The error occurred on line %d of the file %s:\n' % (line_number, src_file)
if src_file not in ('<string>', '<unicode>'):
responsible_line = self._get_line_from_file(src_file, line_number - 1)
if responsible_line:
error_message += responsible_line
error_message += (' ' * (col_number-1)) + '^'
except IOError:
error_message += '\n(could not open file to display line)'
except IndexError:
error_message += '\n(specified line no longer in file, maybe it changed?)'
return error_message
class AnsibleParserError(AnsibleError): class AnsibleParserError(AnsibleError):
''' something was detected early that is wrong about a playbook or data file ''' ''' something was detected early that is wrong about a playbook or data file '''

View file

@ -8,6 +8,9 @@ class AnsibleBaseYAMLObject(object):
_line_number = None _line_number = None
_column_number = None _column_number = None
def get_position_info(self):
return (self._data_source, self._line_number, self._column_number)
class AnsibleMapping(AnsibleBaseYAMLObject, dict): class AnsibleMapping(AnsibleBaseYAMLObject, dict):
''' sub class for dictionaries ''' ''' sub class for dictionaries '''
pass pass