X-Git-Url: https://gerrit.onap.org/r/gitweb?a=blobdiff_plain;f=azure%2Faria%2Faria-extension-cloudify%2Fsrc%2Faria%2Faria%2Fparser%2Fvalidation%2Fissue.py;fp=azure%2Faria%2Faria-extension-cloudify%2Fsrc%2Faria%2Faria%2Fparser%2Fvalidation%2Fissue.py;h=42fc5808a40a50c0efe550937d9c60956cdd4b86;hb=7409dfb144cf2a06210400134d822a1393462b1f;hp=0000000000000000000000000000000000000000;hpb=9e65649dfff8f00dc0a0ef6b10d020ae0e2255ba;p=multicloud%2Fazure.git diff --git a/azure/aria/aria-extension-cloudify/src/aria/aria/parser/validation/issue.py b/azure/aria/aria-extension-cloudify/src/aria/aria/parser/validation/issue.py new file mode 100644 index 0000000..42fc580 --- /dev/null +++ b/azure/aria/aria-extension-cloudify/src/aria/aria/parser/validation/issue.py @@ -0,0 +1,190 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. + +from __future__ import absolute_import # so we can import standard 'collections' + +from ...utils import ( + collections, + type, + threading, + exceptions, + console, + formatting +) + + +class Issue(object): + PLATFORM = 0 + """ + Platform error (e.g. I/O, hardware, a bug in ARIA) + """ + + SYNTAX = 1 + """ + Syntax and format (e.g. YAML, XML, JSON) + """ + + FIELD = 2 + """ + Single field + """ + + BETWEEN_FIELDS = 3 + """ + Relationships between fields within the type (internal grammar) + """ + + BETWEEN_TYPES = 4 + """ + Relationships between types (e.g. inheritance, external grammar) + """ + + BETWEEN_INSTANCES = 5 + """ + Topology (e.g. static requirements and capabilities) + """ + + EXTERNAL = 6 + """ + External (e.g. live requirements and capabilities) + """ + + ALL = 100 + + def __init__(self, message=None, exception=None, location=None, line=None, + column=None, locator=None, snippet=None, level=0): + if message is not None: + self.message = str(message) + elif exception is not None: + self.message = str(exception) + else: + self.message = 'unknown issue' + + self.exception = exception + + if locator is not None: + self.location = locator.location + self.line = locator.line + self.column = locator.column + else: + self.location = location + self.line = line + self.column = column + + self.snippet = snippet + self.level = level + + @property + def as_raw(self): + return collections.OrderedDict(( + ('level', self.level), + ('message', self.message), + ('location', self.location), + ('line', self.line), + ('column', self.column), + ('snippet', self.snippet), + ('exception', type.full_type_name(self.exception) if self.exception else None))) + + @property + def locator_as_str(self): + if self.location is not None: + if self.line is not None: + if self.column is not None: + return '"%s":%d:%d' % (self.location, self.line, self.column) + else: + return '"%s":%d' % (self.location, self.line) + else: + return '"%s"' % self.location + else: + return None + + @property + def heading_as_str(self): + return '%d: %s' % (self.level, self.message) + + @property + def details_as_str(self): + details_str = '' + locator = self.locator_as_str + if locator is not None: + details_str += '@%s' % locator + if self.snippet is not None: + details_str += '\n%s' % self.snippet + return details_str + + def __str__(self): + heading_str = self.heading_as_str + details = self.details_as_str + if details: + heading_str += ', ' + details + return heading_str + + +class ReporterMixin(object): + + Issue = Issue + + def __init__(self, *args, **kwargs): + super(ReporterMixin, self).__init__(*args, **kwargs) + self._issues = threading.LockedList() + self.max_level = self.Issue.ALL + + def report(self, message=None, exception=None, location=None, line=None, + column=None, locator=None, snippet=None, level=Issue.PLATFORM, issue=None): + if issue is None: + issue = self.Issue(message, exception, location, line, column, locator, snippet, level) + + # Avoid duplicate issues + with self._issues: + for i in self._issues: + if str(i) == str(issue): + return + + self._issues.append(issue) + + @property + def has_issues(self): + return len(self._issues) > 0 + + @property + def issues(self): + issues = [i for i in self._issues if i.level <= self.max_level] + issues.sort(key=lambda i: (i.level, i.location, i.line, i.column, i.message)) + return collections.FrozenList(issues) + + @property + def issues_as_raw(self): + return [formatting.as_raw(i) for i in self.issues] + + def extend_issues(self, *issues): + with self._issues: + self._issues.extend(*issues) + + def dump_issues(self): + issues = self.issues + if issues: + console.puts(console.Colored.blue('Validation issues:', bold=True)) + with console.indent(2): + for issue in issues: + console.puts(console.Colored.blue(issue.heading_as_str)) + details = issue.details_as_str + if details: + with console.indent(3): + console.puts(details) + if issue.exception is not None: + with console.indent(3): + exceptions.print_exception(issue.exception) + return True + return False