mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-07-23 05:10:22 -07:00
Adding a data parsing class for v2
This commit is contained in:
parent
c86851be2c
commit
7cb489eca3
12 changed files with 406 additions and 262 deletions
|
@ -21,11 +21,30 @@ __metaclass__ = type
|
|||
|
||||
import os
|
||||
|
||||
from ansible.parsing.yaml.strings import *
|
||||
|
||||
class AnsibleError(Exception):
|
||||
def __init__(self, message, obj=None):
|
||||
# we import this here to prevent an import loop with errors
|
||||
'''
|
||||
This is the base class for all errors raised from Ansible code,
|
||||
and can be instantiated with two optional parameters beyond the
|
||||
error message to control whether detailed information is displayed
|
||||
when the error occurred while parsing a data file of some kind.
|
||||
|
||||
Usage:
|
||||
|
||||
raise AnsibleError('some message here', obj=obj, show_content=True)
|
||||
|
||||
Where "obj" is some subclass of ansible.parsing.yaml.objects.AnsibleBaseYAMLObject,
|
||||
which should be returned by the DataLoader() class.
|
||||
'''
|
||||
|
||||
def __init__(self, message, obj=None, show_content=True):
|
||||
# we import this here to prevent an import loop problem,
|
||||
# since the objects code also imports ansible.errors
|
||||
from ansible.parsing.yaml.objects import AnsibleBaseYAMLObject
|
||||
self._obj = obj
|
||||
|
||||
self._obj = obj
|
||||
self._show_content = show_content
|
||||
if isinstance(self._obj, AnsibleBaseYAMLObject):
|
||||
extended_error = self._get_extended_error()
|
||||
if extended_error:
|
||||
|
@ -36,22 +55,80 @@ class AnsibleError(Exception):
|
|||
def __repr__(self):
|
||||
return self.message
|
||||
|
||||
def _get_line_from_file(self, filename, line_number):
|
||||
with open(filename, 'r') as f:
|
||||
def _get_error_lines_from_file(self, file_name, line_number):
|
||||
'''
|
||||
Returns the line in the file which coresponds to the reported error
|
||||
location, as well as the line preceeding it (if the error did not
|
||||
occur on the first line), to provide context to the error.
|
||||
'''
|
||||
|
||||
target_line = ''
|
||||
prev_line = ''
|
||||
|
||||
with open(file_name, 'r') as f:
|
||||
lines = f.readlines()
|
||||
return lines[line_number]
|
||||
|
||||
target_line = lines[line_number]
|
||||
if line_number > 0:
|
||||
prev_line = lines[line_number - 1]
|
||||
|
||||
return (target_line, prev_line)
|
||||
|
||||
def _get_extended_error(self):
|
||||
'''
|
||||
Given an object reporting the location of the exception in a file, return
|
||||
detailed information regarding it including:
|
||||
|
||||
* the line which caused the error as well as the one preceeding it
|
||||
* causes and suggested remedies for common syntax errors
|
||||
|
||||
If this error was created with show_content=False, the reporting of content
|
||||
is suppressed, as the file contents may be sensitive (ie. vault data).
|
||||
'''
|
||||
|
||||
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)) + '^'
|
||||
error_message += YAML_POSITION_DETAILS % (src_file, line_number, col_number)
|
||||
if src_file not in ('<string>', '<unicode>') and self._show_content:
|
||||
(target_line, prev_line) = self._get_error_lines_from_file(src_file, line_number - 1)
|
||||
if target_line:
|
||||
stripped_line = target_line.replace(" ","")
|
||||
arrow_line = (" " * (col_number-1)) + "^"
|
||||
error_message += "%s\n%s\n%s\n" % (prev_line.rstrip(), target_line.rstrip(), arrow_line)
|
||||
|
||||
# common error/remediation checking here:
|
||||
# check for unquoted vars starting lines
|
||||
if ('{{' in target_line and '}}' in target_line) and ('"{{' not in target_line or "'{{" not in target_line):
|
||||
error_message += YAML_COMMON_UNQUOTED_VARIABLE_ERROR
|
||||
# check for common dictionary mistakes
|
||||
elif ":{{" in stripped_line and "}}" in stripped_line:
|
||||
error_message += YAML_COMMON_DICT_ERROR
|
||||
# check for common unquoted colon mistakes
|
||||
elif len(target_line) and len(target_line) > 1 and len(target_line) > col_number and target_line[col_number] == ":" and target_line.count(':') > 1:
|
||||
error_message += YAML_COMMON_UNQUOTED_COLON_ERROR
|
||||
# otherwise, check for some common quoting mistakes
|
||||
else:
|
||||
parts = target_line.split(":")
|
||||
if len(parts) > 1:
|
||||
middle = parts[1].strip()
|
||||
match = False
|
||||
unbalanced = False
|
||||
|
||||
if middle.startswith("'") and not middle.endswith("'"):
|
||||
match = True
|
||||
elif middle.startswith('"') and not middle.endswith('"'):
|
||||
match = True
|
||||
|
||||
if len(middle) > 0 and middle[0] in [ '"', "'" ] and middle[-1] in [ '"', "'" ] and target_line.count("'") > 2 or target_line.count('"') > 2:
|
||||
unbalanced = True
|
||||
|
||||
if match:
|
||||
error_message += YAML_COMMON_PARTIALLY_QUOTED_LINE_ERROR
|
||||
if unbalanced:
|
||||
error_message += YAML_COMMON_UNBALANCED_QUOTES_ERROR
|
||||
|
||||
except IOError:
|
||||
error_message += '\n(could not open file to display line)'
|
||||
except IndexError:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue