From 7515515cb889c6bb8435c7eae923891d6b28dd8e Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Thu, 16 Mar 2023 19:12:40 +0100 Subject: main: allow -c to be used multiple times Merge values from multiple files. --- alot/__main__.py | 6 +++--- alot/settings/manager.py | 15 +++++++++------ alot/settings/theme.py | 2 +- alot/settings/utils.py | 43 +++++++++++++++++++++++-------------------- alot/utils/argparse.py | 16 +++++++++++++++- 5 files changed, 51 insertions(+), 31 deletions(-) diff --git a/alot/__main__.py b/alot/__main__.py index d9c69149..1b037ec3 100644 --- a/alot/__main__.py +++ b/alot/__main__.py @@ -31,7 +31,7 @@ def parser(): parser.add_argument('-r', '--read-only', action='store_true', help='open notmuch database in read-only mode') parser.add_argument('-c', '--config', metavar='FILENAME', - action=cargparse.ValidatedStoreAction, + action=cargparse.ValidatedAppendAction, validator=cargparse.require_file, help='configuration file') parser.add_argument('-n', '--notmuch-config', metavar='FILENAME', @@ -101,12 +101,12 @@ def main(): # locate alot config files cpath = options.config - if options.config is None: + if cpath is None: xdg_dir = get_xdg_env('XDG_CONFIG_HOME', os.path.expanduser('~/.config')) alotconfig = os.path.join(xdg_dir, 'alot', 'config') if os.path.exists(alotconfig): - cpath = alotconfig + cpath = [alotconfig] try: settings.read_config(cpath) diff --git a/alot/settings/manager.py b/alot/settings/manager.py index 15f75cd0..d7a4b499 100644 --- a/alot/settings/manager.py +++ b/alot/settings/manager.py @@ -112,11 +112,12 @@ class SettingsManager: self._notmuchconfig = None self._config = ConfigObj() self._bindings = None + self._config_paths = [] def reload(self): """Reload notmuch and alot config files""" self.read_notmuch_config(self._notmuchconfig.filename) - self.read_config(self._config.filename) + self.read_config(self._config_paths) def read_notmuch_config(self, path): """ @@ -125,7 +126,7 @@ class SettingsManager: :type path: str """ spec = os.path.join(DEFAULTSPATH, 'notmuch.rc.spec') - self._notmuchconfig = read_config(path, spec) + self._notmuchconfig = read_config([path], spec) def _update_bindings(self, newbindings): assert isinstance(newbindings, Section) @@ -134,14 +135,14 @@ class SettingsManager: 'default.bindings')) self._bindings.merge(newbindings) - def read_config(self, path): + def read_config(self, paths): """ parse alot's config file - :param path: path to alot's config file - :type path: str + :param paths: list of config file paths + :type path: list(str) """ spec = os.path.join(DEFAULTSPATH, 'alot.rc.spec') - newconfig = read_config(path, spec, report_extra=True, checks={ + newconfig = read_config(paths, spec, report_extra=True, checks={ 'mail_container': checks.mail_container, 'force_list': checks.force_list, 'align': checks.align_mode, @@ -150,6 +151,8 @@ class SettingsManager: self._config.merge(newconfig) self._config.walk(self._expand_config_values) + self._config_paths = paths + hooks_path = os.path.expanduser(self._config.get('hooksfile')) if os.path.isfile(hooks_path): try: diff --git a/alot/settings/theme.py b/alot/settings/theme.py index 0eda72f2..4dcacf7a 100644 --- a/alot/settings/theme.py +++ b/alot/settings/theme.py @@ -22,7 +22,7 @@ class Theme: :raises: :class:`~alot.settings.errors.ConfigError` """ self._spec = os.path.join(DEFAULTSPATH, 'theme.spec') - self._config = read_config(path, self._spec, report_extra=True, + self._config = read_config([path], self._spec, report_extra=True, checks={'align': checks.align_mode, 'widthtuple': checks.width_tuple, 'force_list': checks.force_list, diff --git a/alot/settings/utils.py b/alot/settings/utils.py index 316083aa..c7c5bffa 100644 --- a/alot/settings/utils.py +++ b/alot/settings/utils.py @@ -11,13 +11,14 @@ from validate import Validator from .errors import ConfigError -def read_config(configpath=None, specpath=None, checks=None, +def read_config(configs = None, specpath=None, checks=None, report_extra=False): """ get a (validated) config object for given config file path. - :param configpath: path to config-file or a list of lines as its content - :type configpath: str or list(str) + :param configs: list of configurations; each configuration is either a path + to a config file or a list of lines to parse as configuration + :type configs: list :param specpath: path to spec-file :type specpath: str :param checks: custom checks to use for validator. @@ -30,20 +31,23 @@ def read_config(configpath=None, specpath=None, checks=None, """ checks = checks or {} - try: - config = ConfigObj(infile=configpath, configspec=specpath, - file_error=True, encoding='UTF8') - except ConfigObjError as e: - msg = 'Error when parsing `%s`:\n%s' % (configpath, e) - logging.error(msg) - raise ConfigError(msg) - except IOError: - raise ConfigError('Could not read %s and/or %s' - % (configpath, specpath)) - except UnboundLocalError: - # this works around a bug in configobj - msg = '%s is malformed. Check for sections without parents..' - raise ConfigError(msg % configpath) + config = ConfigObj(configspec = specpath) + + for cf in configs: + try: + c = ConfigObj(infile = cf, configspec = specpath, + file_error = True, encoding = 'UTF8') + except ConfigObjError as e: + msg = 'Error when parsing `%s`:\n%s' % (cf, e) + logging.error(msg) + raise ConfigError(msg) + except IOError: + raise ConfigError('Could not read %s and/or %s' % (cf, specpath)) + except UnboundLocalError: + # this works around a bug in configobj + msg = '%s is malformed. Check for sections without parents..' % cf + raise ConfigError(msg) + config.merge(c) if specpath: validator = Validator() @@ -70,9 +74,8 @@ def read_config(configpath=None, specpath=None, checks=None, extra_values = get_extra_values(config) if report_extra else None if extra_values: - msg = ['Unknown values were found in `%s`. Please check for ' - 'typos if a specified setting does not seem to work:' - % configpath] + msg = ['Unknown values were found in the config. Please check for ' + 'typos if a specified setting does not seem to work:'] for sections, val in extra_values: if sections: msg.append('%s: %s' % ('->'.join(sections), val)) diff --git a/alot/utils/argparse.py b/alot/utils/argparse.py index b8e3d87d..f58b7a75 100644 --- a/alot/utils/argparse.py +++ b/alot/utils/argparse.py @@ -116,7 +116,7 @@ class BooleanAction(argparse.Action): setattr(namespace, self.dest, values) -class ValidatedStoreAction(argparse.Action): +class _ValidatedAction(argparse.Action): """An action that allows a validation function to be specificied. The validator keyword must be a function taking exactly one argument, that @@ -128,6 +128,9 @@ class ValidatedStoreAction(argparse.Action): super().__init__(option_strings, dest, **kwargs) self.validator = validator + def _do_action(self, namespace, values): + raise NotImplementedError + def __call__(self, parser, namespace, values, option_string=None): if self.validator: try: @@ -135,4 +138,15 @@ class ValidatedStoreAction(argparse.Action): except ValidationFailed as e: raise argparse.ArgumentError(self, str(e)) + self._do_action(namespace, values) + +class ValidatedStoreAction(_ValidatedAction): + def _do_action(self, namespace, values): setattr(namespace, self.dest, values) +class ValidatedAppendAction(_ValidatedAction): + def _do_action(self, namespace, values): + items = getattr(namespace, self.dest) + if items is None: + items = [] + items.append(values) + setattr(namespace, self.dest, items) -- cgit v1.2.3