fune/python/mozbuild/mozpack/errors.py
Ricky Stewart 02a7b4ebdf Bug 1654103: Standardize on Black for Python code in mozilla-central.
Allow-list all Python code in tree for use with the black linter, and re-format all code in-tree accordingly.

To produce this patch I did all of the following:

1. Make changes to tools/lint/black.yml to remove include: stanza and update list of source extensions.

2. Run ./mach lint --linter black --fix

3. Make some ad-hoc manual updates to python/mozbuild/mozbuild/test/configure/test_configure.py -- it has some hard-coded line numbers that the reformat breaks.

4. Make some ad-hoc manual updates to `testing/marionette/client/setup.py`, `testing/marionette/harness/setup.py`, and `testing/firefox-ui/harness/setup.py`, which have hard-coded regexes that break after the reformat.

5. Add a set of exclusions to black.yml. These will be deleted in a follow-up bug (1672023).

# ignore-this-changeset

Differential Revision: https://phabricator.services.mozilla.com/D94045
2020-10-26 18:34:53 +00:00

140 lines
4.1 KiB
Python

# 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, print_function, unicode_literals
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(msg, file=self.out)
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()