tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

errors.py (4215B)


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