nicoulaj/rainbow

View on GitHub
rainbow/config/parser.py

Summary

Maintainability
C
1 day
Test Coverage
A
100%
# ----------------------------------------------------------------------
# rainbow, a terminal colorizer - https://github.com/nicoulaj/rainbow
# copyright (c) 2010-2018 rainbow contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
# ----------------------------------------------------------------------


from os.path import dirname

from rainbow import LOGGER
from rainbow.config.locator import ConfigLocator
from rainbow.filter import FILTERS_BY_NAME, FILTERS_BY_SHORT_OPTION, FILTERS_BY_LONG_OPTION

try:
    import configparser
except ImportError:  # no cover
    import ConfigParser as configparser


class ConfigParser(object):
    def __init__(self,
                 stdout_builder,
                 stderr_builder,
                 paths=None,
                 error_handler=lambda error: None):
        self.locator = ConfigLocator(paths)
        self.stdout_builder = stdout_builder
        self.stderr_builder = stderr_builder
        self.error_handler = error_handler

    def parse_file(self, config_file):

        LOGGER.debug('Loading the config file "%s"', config_file)

        config_parser = configparser.ConfigParser()
        try:
            if not config_parser.read(config_file):
                self.error_handler('Could not open config file "%s"' % config_file)
                return
        except configparser.DuplicateSectionError as e:  # no cover (inconsistent between Python 2 and 3)
            self.error_handler('Duplicate section "%s" in "%s"' % (e.section, config_file))
            return

        enable_stderr_filtering = True

        for section in config_parser.sections():

            if section == 'general':

                for key, value in config_parser.items(section):

                    if key == 'imports':
                        if not value:
                            self.error_handler('Empty imports section in config "%s"' % config_file)
                        else:
                            for config_import in [v.strip() for v in value.split(',')]:
                                if not config_import:
                                    self.error_handler('Empty import in config "%s"' % config_file)
                                else:
                                    config_import_file = self.locator.locate_config_file(config_import,
                                                                                         dirname(config_file))
                                    if config_import_file:
                                        self.parse_file(config_import_file)
                                    else:
                                        self.error_handler('Failed to resolve import of "%s" in config "%s"'
                                                           % (config_import, config_file))

                    elif key == 'enable-stderr-filtering':
                        try:
                            enable_stderr_filtering = config_parser.getboolean(section, 'enable-stderr-filtering')
                        except ValueError:
                            self.error_handler(
                                'Invalid value "%s" for key "%s" in config "%s"' % (value, key, config_file))

                    else:
                        self.error_handler('Invalid key "%s" in general section of config "%s"' % (key, config_file))

            elif section == 'filters':

                for filter_name, pattern_lines in config_parser.items(section):

                    resolved_filter = \
                        FILTERS_BY_NAME.get(filter_name) or \
                        FILTERS_BY_LONG_OPTION.get(filter_name) or \
                        FILTERS_BY_SHORT_OPTION.get(filter_name)

                    if not resolved_filter:
                        self.error_handler('Unknown filter "%s" in config "%s"' % (filter_name, config_file))
                        continue

                    if not pattern_lines:
                        self.error_handler('Empty pattern for "%s" in config "%s"' % (filter_name, config_file))
                        continue

                    for pattern in pattern_lines.splitlines():
                        self.stdout_builder.add_mapping(pattern, resolved_filter)
                        if enable_stderr_filtering:
                            self.stderr_builder.add_mapping(pattern, resolved_filter)

            else:
                self.error_handler('Invalid section "%s" in config "%s"' % (section, config_file))

        LOGGER.info('Loaded config "%s"', config_file)