summaryrefslogtreecommitdiffstats
path: root/peekaboo/config.py
diff options
context:
space:
mode:
Diffstat (limited to 'peekaboo/config.py')
-rw-r--r--peekaboo/config.py288
1 files changed, 159 insertions, 129 deletions
diff --git a/peekaboo/config.py b/peekaboo/config.py
index 9215011..51a4870 100644
--- a/peekaboo/config.py
+++ b/peekaboo/config.py
@@ -26,6 +26,7 @@
defaults as well as reading a configuration file. """
+import re
import sys
import logging
import configparser
@@ -55,8 +56,160 @@ class PeekabooConfigParser( # pylint: disable=too-many-ancestors
'Configuration file "%s" can not be parsed: %s' %
(config_file, cperror))
+ self.lists = {}
+ self.relists = {}
+
+ def getlist(self, section, option, raw=False, vars=None, fallback=None):
+ """ Special getter where multiple options in the config file
+ distinguished by a .<no> suffix form a list. Matches the signature for
+ configparser getters. """
+ # cache results because the following is somewhat inefficient
+ if section not in self.lists:
+ self.lists[section] = {}
+
+ if option in self.lists[section]:
+ return self.lists[section][option]
+
+ if section not in self:
+ self.lists[section][option] = fallback
+ return fallback
+
+ # Go over all options in this section we want to allow "holes" in
+ # the lists, i.e setting.1, setting.2 but no setting.3 followed by
+ # setting.4. We use here that ConfigParser retains option order from
+ # the file.
+ value = []
+ for setting in self[section]:
+ if not setting.startswith(option):
+ continue
+
+ # Parse 'setting' into (key) and 'setting.subscript' into
+ # (key, subscript) and use it to determine if this setting is a
+ # list. Note how we do not use the subscript at all here.
+ name_parts = setting.split('.')
+ key = name_parts[0]
+ is_list = len(name_parts) > 1
+
+ if key != option:
+ continue
+
+ if not is_list:
+ raise PeekabooConfigException(
+ 'Option %s in section %s is supposed to be a list '
+ 'but given as individual setting' % (setting, section))
+
+ # Potential further checks:
+ # - There are no duplicate settings with ConfigParser. The last
+ # one always wins.
+
+ value.append(self[section].get(setting, raw=raw, vars=vars))
+
+ # it's not gonna get any better on the next call, so cache even the
+ # default
+ if not value:
+ value = fallback
+
+ self.lists[section][option] = value
+ return value
+
+ def getrelist(self, section, option, raw=False, vars=None, fallback=None):
+ """ Special getter for lists of regular expressions. Returns the
+ compiled expression objects in a list ready for matching and searching.
+ """
+ if section not in self.relists:
+ self.relists[section] = {}
+
+ if option in self.relists[section]:
+ return self.relists[section][option]
+
+ if section not in self:
+ self.relists[section][option] = fallback
+ return fallback
+
+ strlist = self[section].getlist(option, raw=raw, vars=vars,
+ fallback=fallback)
+ if strlist is None:
+ self.relists[section][option] = None
+ return None
+
+ compiled_res = []
+ for regex in strlist:
+ try:
+ compiled_res.append(re.compile(regex))
+ except (ValueError, TypeError) as error:
+ raise PeekabooConfigException(
+ 'Failed to compile regular expression "%s" (section %s, '
+ 'option %s): %s' % (re, section, option, error))
+
+ # it's not gonna get any better on the next call, so cache even the
+ # default
+ if not compiled_res:
+ compiled_res = fallback
+
+ self.relists[section][option] = compiled_res
+ return compiled_res
+
+ def get_log_level(self, section, option, raw=False, vars=None,
+ fallback=None):
+ """ Get the log level from the configuration file and parse the string
+ into a logging loglevel such as logging.CRITICAL. Raises config
+ exception if the log level is unknown. Options identical to get(). """
+ levels = {
+ 'CRITICAL': logging.CRITICAL,
+ 'ERROR': logging.ERROR,
+ 'WARNING': logging.WARNING,
+ 'INFO': logging.INFO,
+ 'DEBUG': logging.DEBUG
+ }
+
+ level = self.get(section, option, raw=raw, vars=vars, fallback=None)
+ if level is None:
+ return fallback
-class PeekabooConfig(object): # pylint: disable=too-many-instance-attributes
+ if level not in levels:
+ raise PeekabooConfigException('Unknown log level %s' % level)
+
+ return levels[level]
+
+ def get_by_default_type(self, section, option, fallback=None,
+ option_type=None):
+ """ Get an option from the configuration file parser. Automatically
+ detects the type from the type of the default if given and calls the
+ right getter method to coerce the value to the correct type.
+
+ @param section: Which section to look for option in.
+ @type section: string
+ @param option: The option to read.
+ @type option: string
+ @param fallback: (optional) Default value to return if option is not
+ found. Defaults itself to None so that the method will
+ return None if the option is not found.
+ @type fallback: int, bool, str or None.
+ @param option_type: Override the option type.
+ @type option_type: int, bool, str or None. """
+ if option_type is None and fallback is not None:
+ option_type = type(fallback)
+
+ getter = {
+ int: self.getint,
+ bool: self.getboolean,
+ str: self.get,
+ None: self.get,
+ }
+
+ try:
+ return getter[option_type](section, option)
+ except configparser.NoSectionError:
+ logger.debug('Configuration section %s not found - using '
+ 'default %s', section, fallback)
+ except configparser.NoOptionError:
+ logger.debug('Configuration option %s not found in section '
+ '%s - using default: %s', option, section, fallback)
+
+ return fallback
+
+
+class PeekabooConfig(PeekabooConfigParser):
""" This class represents the Peekaboo configuration. """
def __init__(self, config_file=None, log_level=None):
""" Initialise the configuration with defaults, overwrite with command
@@ -142,21 +295,21 @@ class PeekabooConfig(object): # pylint: disable=too-many-instance-attributes
# read configuration file. Note that we require a configuration file
# here. We may change that if we decide that we want to allow the user
# to run us with the above defaults only.
- self.__config = PeekabooConfigParser(self.config_file)
+ PeekabooConfigParser.__init__(self, self.config_file)
# overwrite above defaults in our member variables via indirect access
settings = vars(self)
for (option, config_names) in config_options.items():
# maybe use special getter
- get = self.get
+ getter = self.get_by_default_type
if len(config_names) == 3:
- get = config_names[2]
+ getter = config_names[2]
# e.g.:
# self.log_format = self.get('logging', 'log_format',
# self.log_format)
- settings[option] = get(config_names[0], config_names[1],
- settings[option])
+ settings[option] = getter(
+ config_names[0], config_names[1], fallback=settings[option])
# Update logging with what we just parsed from the config
self.setup_logging()
@@ -164,63 +317,6 @@ class PeekabooConfig(object): # pylint: disable=too-many-instance-attributes
# here we could overwrite defaults and config file with additional
# command line arguments if required
- def get(self, section, option, default=None, option_type=None):
- """ Get an option from the configuration file parser. Automatically
- detects the type from the type of the default if given and calls the
- right getter method to coerce the value to the correct type.
-
- @param section: Which section to look for option in.
- @type section: string
- @param option: The option to read.
- @type option: string
- @param default: (optional) Default value to return if option is not
- found. Defaults itself to None so that the method will
- return None if the option is not found.
- @type default: int, bool, str or None.
- @param option_type: Override the option type.
- @type option_type: int, bool, str or None. """
- if option_type is None and default is not None:
- option_type = type(default)
-
- getter = {
- int: self.__config.getint,
- bool: self.__config.getboolean,
- str: self.__config.get,
- None: self.__config.get,
- }
-
- try:
- return getter[option_type](section, option)
- except configparser.NoSectionError:
- logger.debug('Configuration section %s not found - using '
- 'default %s', section, default)
- except configparser.NoOptionError:
- logger.debug('Configuration option %s not found in section '
- '%s - using default: %s', option, section, default)
-
- return default
-
- def get_log_level(self, section, option, default=None):
- """ Get the log level from the configuration file and parse the string
- into a logging loglevel such as logging.CRITICAL. Raises config
- exception if the log level is unknown. Options identical to get(). """
- levels = {
- 'CRITICAL': logging.CRITICAL,
- 'ERROR': logging.ERROR,
- 'WARNING': logging.WARNING,
- 'INFO': logging.INFO,
- 'DEBUG': logging.DEBUG
- }
-
- level = self.get(section, option, None)
- if level is None:
- return default
-
- if level not in levels:
- raise PeekabooConfigException('Unknown log level %s' % level)
-
- return levels[level]
-
def setup_logging(self):
""" Setup logging to console by reconfiguring the root logger so that
it affects all loggers everywhere. """
@@ -248,69 +344,3 @@ class PeekabooConfig(object): # pylint: disable=too-many-instance-attributes
return '<PeekabooConfig(%s)>' % settings
__repr__ = __str__
-
-
-class PeekabooRulesetConfig(object):
- """
- This class represents the ruleset configuration file "ruleset.conf".
-
- The ruleset configuration is stored as a dictionary in the form of
- ruleset_config[rule_name][config_option] = value | [value1, value2, ...]
-
- @since: 1.6
- """
- def __init__(self, config_file):
- self.config_file = config_file
- self.ruleset_config = {}
-
- config = PeekabooConfigParser(self.config_file)
- sections = config.sections()
- for section in sections:
- self.ruleset_config[section] = {}
-
- for section in sections:
- for setting in config.options(section):
- # Parse 'setting' into (key) and 'setting.subscript' into
- # (key, subscript) and use it to determine if this setting is a
- # list. Note how we do not use the subscript at all here.
- name_parts = setting.split('.')
- key = name_parts[0]
- is_list = len(name_parts) > 1
-
- saved_val = self.ruleset_config[section].get(key)
- if saved_val is None and is_list:
- saved_val = []
-
- # If the setting wants to add to a list the saved or freshly
- # initialised value from above should be a list. Otherwise it
- # should of course not be.
- if is_list != isinstance(saved_val, list):
- raise PeekabooConfigException(
- 'Setting %s in section %s specified as list as well '
- 'as individual setting' % (setting, section))
-
- # Potential further checks:
- # - There are no duplicate settings with ConfigParser. The last
- # one always wins.
-
- if is_list:
- saved_val.append(config.get(section, setting))
- else:
- saved_val = config.get(section, setting)
-
- self.ruleset_config[section][key] = saved_val
-
- def rule_config(self, rule):
- """ Get the configuration for a rule.
-
- @param rule: Name of the rule whose configuration to return.
- @type rule: string
- @return: dict of rule configuration settings or None if no
- configuration is present. """
- return self.ruleset_config.get(rule)
-
- def __str__(self):
- return '<PeekabooRulesetConfiguration(filepath="%s", %s)>' % \
- (self.config_file, self.ruleset_config)
-
- __repr__ = __str__