2013-01-23 02:23:14 -08:00
|
|
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
|
|
|
|
import sys
|
|
|
|
from contextlib import contextmanager
|
|
|
|
|
|
|
|
|
|
|
|
class ErrorMessage(Exception):
|
|
|
|
'''Exception type raised from errors.error() and errors.fatal()'''
|
|
|
|
|
|
|
|
|
|
|
|
class AccumulatedErrors(Exception):
|
|
|
|
'''Exception type raised from errors.accumulate()'''
|
|
|
|
|
|
|
|
|
|
|
|
class ErrorCollector(object):
|
|
|
|
'''
|
|
|
|
Error handling/logging class. A global instance, errors, is provided for
|
|
|
|
convenience.
|
|
|
|
|
|
|
|
Warnings, errors and fatal errors may be logged by calls to the following
|
|
|
|
functions:
|
|
|
|
errors.warn(message)
|
|
|
|
errors.error(message)
|
|
|
|
errors.fatal(message)
|
|
|
|
|
|
|
|
Warnings only send the message on the logging output, while errors and
|
|
|
|
fatal errors send the message and throw an ErrorMessage exception. The
|
|
|
|
exception, however, may be deferred. See further below.
|
|
|
|
|
|
|
|
Errors may be ignored by calling:
|
|
|
|
errors.ignore_errors()
|
|
|
|
|
|
|
|
After calling that function, only fatal errors throw an exception.
|
|
|
|
|
|
|
|
The warnings, errors or fatal errors messages may be augmented with context
|
|
|
|
information when a context is provided. Context is defined by a pair
|
|
|
|
(filename, linenumber), and may be set with errors.context() used as a
|
|
|
|
context manager:
|
|
|
|
with errors.context(filename, linenumber):
|
|
|
|
errors.warn(message)
|
|
|
|
|
|
|
|
Arbitrary nesting is supported, both for errors.context calls:
|
|
|
|
with errors.context(filename1, linenumber1):
|
|
|
|
errors.warn(message)
|
|
|
|
with errors.context(filename2, linenumber2):
|
|
|
|
errors.warn(message)
|
|
|
|
|
|
|
|
as well as for function calls:
|
|
|
|
def func():
|
|
|
|
errors.warn(message)
|
|
|
|
with errors.context(filename, linenumber):
|
|
|
|
func()
|
|
|
|
|
|
|
|
Errors and fatal errors can have their exception thrown at a later time,
|
|
|
|
allowing for several different errors to be reported at once before
|
|
|
|
throwing. This is achieved with errors.accumulate() as a context manager:
|
|
|
|
with errors.accumulate():
|
|
|
|
if test1:
|
|
|
|
errors.error(message1)
|
|
|
|
if test2:
|
|
|
|
errors.error(message2)
|
|
|
|
|
|
|
|
In such cases, a single AccumulatedErrors exception is thrown, but doesn't
|
|
|
|
contain information about the exceptions. The logged messages do.
|
|
|
|
'''
|
|
|
|
out = sys.stderr
|
|
|
|
WARN = 1
|
|
|
|
ERROR = 2
|
|
|
|
FATAL = 3
|
|
|
|
_level = ERROR
|
|
|
|
_context = []
|
|
|
|
_count = None
|
|
|
|
|
|
|
|
def ignore_errors(self, ignore=True):
|
|
|
|
if ignore:
|
|
|
|
self._level = self.FATAL
|
|
|
|
else:
|
|
|
|
self._level = self.ERROR
|
|
|
|
|
|
|
|
def _full_message(self, level, msg):
|
|
|
|
if level >= self._level:
|
|
|
|
level = 'Error'
|
|
|
|
else:
|
|
|
|
level = 'Warning'
|
|
|
|
if self._context:
|
|
|
|
file, line = self._context[-1]
|
|
|
|
return "%s: %s:%d: %s" % (level, file, line, msg)
|
|
|
|
return "%s: %s" % (level, msg)
|
|
|
|
|
|
|
|
def _handle(self, level, msg):
|
|
|
|
msg = self._full_message(level, msg)
|
|
|
|
if level >= self._level:
|
|
|
|
if self._count is None:
|
|
|
|
raise ErrorMessage(msg)
|
|
|
|
self._count += 1
|
|
|
|
print >>self.out, msg
|
|
|
|
|
|
|
|
def fatal(self, msg):
|
|
|
|
self._handle(self.FATAL, msg)
|
|
|
|
|
|
|
|
def error(self, msg):
|
|
|
|
self._handle(self.ERROR, msg)
|
|
|
|
|
|
|
|
def warn(self, msg):
|
|
|
|
self._handle(self.WARN, msg)
|
|
|
|
|
|
|
|
def get_context(self):
|
|
|
|
if self._context:
|
|
|
|
return self._context[-1]
|
|
|
|
|
|
|
|
@contextmanager
|
|
|
|
def context(self, file, line):
|
|
|
|
if file and line:
|
|
|
|
self._context.append((file, line))
|
|
|
|
yield
|
|
|
|
if file and line:
|
|
|
|
self._context.pop()
|
|
|
|
|
|
|
|
@contextmanager
|
|
|
|
def accumulate(self):
|
|
|
|
assert self._count is None
|
|
|
|
self._count = 0
|
|
|
|
yield
|
|
|
|
count = self._count
|
|
|
|
self._count = None
|
|
|
|
if count:
|
|
|
|
raise AccumulatedErrors()
|
|
|
|
|
2013-02-02 22:19:15 -08:00
|
|
|
@property
|
|
|
|
def count(self):
|
|
|
|
# _count can be None.
|
|
|
|
return self._count if self._count else 0
|
|
|
|
|
2013-01-23 02:23:14 -08:00
|
|
|
|
|
|
|
errors = ErrorCollector()
|