Source code for kuha_common.conf

"""Parse command line and store application wide settings.

Typically used to get configuration values using configargparse.
However, the module can be used to manually setup configuration values.

Get configuration using configargparse::

    >>> load('myprogram')
    >>> add('--some-arg')
    >>> settings = get_conf()

Manually set configuration values::

    >>> add_conf('some_arg', 'some_value')
    >>> settings = get_conf()
"""
import os
import configargparse


_STORED = {'conf': configargparse.Namespace(),
           'default_configfile_paths': [],
           'package': None,
           'env_var_prefix': None,
           'parser': None}
_DEFAULT_CFG_FILE = '{prog}.ini'
LOG_FORMAT = '%(message)s'
LOG_LEVELS = ['CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG']


[docs]class CommandLineParseException(Exception): """Raised on exceptions during command line parsing"""
[docs]class KuhaConfigFileParser(configargparse.DefaultConfigFileParser): """Inherit to override :meth:`configargparse.DefaultConfigFileParser.get_syntax_description` """
[docs] def get_syntax_description(self): """Override syntax description of :class:`configargparse.DefaultConfigFileParser` :returns: Syntax description for configuration file. :rtype: str """ msg = ("Config file syntax allows: key=value, flag=true, stuff=[a,b,c], " "and ignores sections ([section]), so all configuration options should " "be considered global in application context. Note that not all " "configuration options, although supported, make sense in " "configuration file, for example help-option.") return msg
def _conf_loaded(): return vars(_STORED['conf']) != {}
[docs]def load(prog, package=None, env_var_prefix=None, **kw): """Load configuration parser. Parameters are passed to :func:`configargparse.get_arg_parser()` and the parser is stored for later use. :param str prog: Program name :param str package: Python package. Use prog if not given. :param str env_var_prefix: Prefix for environment variables. Optional. :param list default_config_files: Default configuration files. Use CWD/PROG.ini if not given. :param str conflict_handler: Conflict handler. Default to 'error'. See configargparse and argparse for details. :param class config_file_parser_class: Configuration file parser class. Defaults to KuhaConfigFileParser. :returns: Loaded parser :rtype: :obj:`configargparse.ArgumentParser` """ if _STORED['parser'] is not None: raise CommandLineParseException("Parser already loaded") if 'default_config_files' not in kw: kw['default_config_files'] = [os.path.join( os.getcwd(), _DEFAULT_CFG_FILE.format(prog=prog))] if 'conflict_handler' not in kw: # For reasons unknown configargparse.init_argument_parser changes # the default argparse conflict_handler from 'error' to 'resolve' # This leads to not raising ArgumentErrors for duplicate option # strings. Change it back to make sure module add() functions # cannot assign conflicting options. kw['conflict_handler'] = 'error' if 'config_file_parser_class' not in kw: kw['config_file_parser_class'] = KuhaConfigFileParser _STORED['package'] = package or prog _STORED['env_var_prefix'] = env_var_prefix or None _STORED['parser'] = configargparse.get_arg_parser(prog=prog, **kw) _STORED['default_configfile_paths'] = kw['default_config_files'] return _STORED['parser']
[docs]def add(*args, **kwargs): """Shorthand adds argument to stored parser All arguments are passed as is to stored parser. If there is a submitted env_var and load was given env_var_prefix, the environment variable keys are prefixed with loaded env_var_prefix. Raises CommandLineParseException if configuration has already been loaded. """ if _STORED['parser'] is None: raise CommandLineParseException("Parser not loaded") if _conf_loaded(): raise CommandLineParseException("Configuration already loaded") if 'env_var' in kwargs and _STORED['env_var_prefix']: kwargs['env_var'] = ''.join([_STORED['env_var_prefix'], kwargs['env_var']]) _STORED['parser'].add(*args, **kwargs)
[docs]def get(key, default=None): """Get configuration value for key or default. Use to get a configuration key from loaded settings. If a key is not present, returns default instead. :param str key: Get key from loaded settings :param default: Return default if key not in settings :returns: Configuration value """ settings = get_conf() if not hasattr(settings, key): return default return getattr(settings, key)
def add_print_arg(): add('--print-configuration', help='Print active configuration and exit.', default=False, action='store_true') def add_config_arg(): add('-c', '--config', is_config_file=True, help='Configuration file path') def _force_env_prefix(env_var): if not _STORED['env_var_prefix']: # Make sure we're not using plain LOGLEVEL env var. env_var = '%s_%s' % (get_prog().upper(), env_var) return env_var def add_loglevel_arg(): add('--loglevel', help='Lowest logging level of log messages to output', default='WARNING', choices=LOG_LEVELS, env_var=_force_env_prefix('LOGLEVEL'), type=str) def add_logformat_arg(): add('--logformat', help='Logging format', default=LOG_FORMAT, env_var=_force_env_prefix('LOGFORMAT'), type=str)
[docs]def add_conf(key, value): """Add configuration key-value pair. Use this to bypass command line parsing. :param str key: Configuration key :param value: Configuration value """ if hasattr(_STORED['conf'], key): raise ValueError("Stored configuration already has a value for '%s'" % (key,)) setattr(_STORED['conf'], key, value)
[docs]def get_conf(conf_key=None): """Parse arguments, store and return them. If arguments are already parsed and stored, return them. .. note:: Throws errors for unrecognized command line arguments and configuration file keys (parse_args()). Use environment variables to enable a single source of truth in project wide scope. :param str conf_key: Optional parameter to return a single configuration value. :returns: Parsed arguments :rtype: :obj:`argparse.Namespace` """ if _conf_loaded() is False and _STORED['parser'] is not None: parser = _STORED['parser'] _STORED['conf'] = parser.parse_args() if conf_key is not None: return getattr(_STORED['conf'], conf_key) return _STORED['conf']
def get_prog(): return _STORED['parser'].prog def get_package(): return _STORED['package']