summaryrefslogtreecommitdiffstats
path: root/azure/aria/aria-extension-cloudify/src/aria/aria/parser/validation/issue.py
diff options
context:
space:
mode:
Diffstat (limited to 'azure/aria/aria-extension-cloudify/src/aria/aria/parser/validation/issue.py')
-rw-r--r--azure/aria/aria-extension-cloudify/src/aria/aria/parser/validation/issue.py190
1 files changed, 190 insertions, 0 deletions
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