123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140 |
- # 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/.
- from __future__ import absolute_import
- 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()
- @property
- def count(self):
- # _count can be None.
- return self._count if self._count else 0
- errors = ErrorCollector()
|