summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton Khirnov <anton@khirnov.net>2023-03-16 19:12:40 +0100
committerAnton Khirnov <anton@khirnov.net>2023-03-16 19:12:40 +0100
commit7515515cb889c6bb8435c7eae923891d6b28dd8e (patch)
treee36e379f040c453497cac535d564d63520580873
parentefec104994051a54dac147889f69c23f9c1bcb1c (diff)
main: allow -c to be used multiple times
Merge values from multiple files.
-rw-r--r--alot/__main__.py6
-rw-r--r--alot/settings/manager.py15
-rw-r--r--alot/settings/theme.py2
-rw-r--r--alot/settings/utils.py43
-rw-r--r--alot/utils/argparse.py16
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)