python/mozbuild/mozpack/errors.py

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

michael@0 1 # This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 # License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
michael@0 4
michael@0 5 import sys
michael@0 6 from contextlib import contextmanager
michael@0 7
michael@0 8
michael@0 9 class ErrorMessage(Exception):
michael@0 10 '''Exception type raised from errors.error() and errors.fatal()'''
michael@0 11
michael@0 12
michael@0 13 class AccumulatedErrors(Exception):
michael@0 14 '''Exception type raised from errors.accumulate()'''
michael@0 15
michael@0 16
michael@0 17 class ErrorCollector(object):
michael@0 18 '''
michael@0 19 Error handling/logging class. A global instance, errors, is provided for
michael@0 20 convenience.
michael@0 21
michael@0 22 Warnings, errors and fatal errors may be logged by calls to the following
michael@0 23 functions:
michael@0 24 errors.warn(message)
michael@0 25 errors.error(message)
michael@0 26 errors.fatal(message)
michael@0 27
michael@0 28 Warnings only send the message on the logging output, while errors and
michael@0 29 fatal errors send the message and throw an ErrorMessage exception. The
michael@0 30 exception, however, may be deferred. See further below.
michael@0 31
michael@0 32 Errors may be ignored by calling:
michael@0 33 errors.ignore_errors()
michael@0 34
michael@0 35 After calling that function, only fatal errors throw an exception.
michael@0 36
michael@0 37 The warnings, errors or fatal errors messages may be augmented with context
michael@0 38 information when a context is provided. Context is defined by a pair
michael@0 39 (filename, linenumber), and may be set with errors.context() used as a
michael@0 40 context manager:
michael@0 41 with errors.context(filename, linenumber):
michael@0 42 errors.warn(message)
michael@0 43
michael@0 44 Arbitrary nesting is supported, both for errors.context calls:
michael@0 45 with errors.context(filename1, linenumber1):
michael@0 46 errors.warn(message)
michael@0 47 with errors.context(filename2, linenumber2):
michael@0 48 errors.warn(message)
michael@0 49
michael@0 50 as well as for function calls:
michael@0 51 def func():
michael@0 52 errors.warn(message)
michael@0 53 with errors.context(filename, linenumber):
michael@0 54 func()
michael@0 55
michael@0 56 Errors and fatal errors can have their exception thrown at a later time,
michael@0 57 allowing for several different errors to be reported at once before
michael@0 58 throwing. This is achieved with errors.accumulate() as a context manager:
michael@0 59 with errors.accumulate():
michael@0 60 if test1:
michael@0 61 errors.error(message1)
michael@0 62 if test2:
michael@0 63 errors.error(message2)
michael@0 64
michael@0 65 In such cases, a single AccumulatedErrors exception is thrown, but doesn't
michael@0 66 contain information about the exceptions. The logged messages do.
michael@0 67 '''
michael@0 68 out = sys.stderr
michael@0 69 WARN = 1
michael@0 70 ERROR = 2
michael@0 71 FATAL = 3
michael@0 72 _level = ERROR
michael@0 73 _context = []
michael@0 74 _count = None
michael@0 75
michael@0 76 def ignore_errors(self, ignore=True):
michael@0 77 if ignore:
michael@0 78 self._level = self.FATAL
michael@0 79 else:
michael@0 80 self._level = self.ERROR
michael@0 81
michael@0 82 def _full_message(self, level, msg):
michael@0 83 if level >= self._level:
michael@0 84 level = 'Error'
michael@0 85 else:
michael@0 86 level = 'Warning'
michael@0 87 if self._context:
michael@0 88 file, line = self._context[-1]
michael@0 89 return "%s: %s:%d: %s" % (level, file, line, msg)
michael@0 90 return "%s: %s" % (level, msg)
michael@0 91
michael@0 92 def _handle(self, level, msg):
michael@0 93 msg = self._full_message(level, msg)
michael@0 94 if level >= self._level:
michael@0 95 if self._count is None:
michael@0 96 raise ErrorMessage(msg)
michael@0 97 self._count += 1
michael@0 98 print >>self.out, msg
michael@0 99
michael@0 100 def fatal(self, msg):
michael@0 101 self._handle(self.FATAL, msg)
michael@0 102
michael@0 103 def error(self, msg):
michael@0 104 self._handle(self.ERROR, msg)
michael@0 105
michael@0 106 def warn(self, msg):
michael@0 107 self._handle(self.WARN, msg)
michael@0 108
michael@0 109 def get_context(self):
michael@0 110 if self._context:
michael@0 111 return self._context[-1]
michael@0 112
michael@0 113 @contextmanager
michael@0 114 def context(self, file, line):
michael@0 115 if file and line:
michael@0 116 self._context.append((file, line))
michael@0 117 yield
michael@0 118 if file and line:
michael@0 119 self._context.pop()
michael@0 120
michael@0 121 @contextmanager
michael@0 122 def accumulate(self):
michael@0 123 assert self._count is None
michael@0 124 self._count = 0
michael@0 125 yield
michael@0 126 count = self._count
michael@0 127 self._count = None
michael@0 128 if count:
michael@0 129 raise AccumulatedErrors()
michael@0 130
michael@0 131 @property
michael@0 132 def count(self):
michael@0 133 # _count can be None.
michael@0 134 return self._count if self._count else 0
michael@0 135
michael@0 136
michael@0 137 errors = ErrorCollector()

mercurial