diff --git a/dotdrop/config.py b/dotdrop/config.py index c10cc1e..c4f6ff0 100644 --- a/dotdrop/config.py +++ b/dotdrop/config.py @@ -5,7 +5,6 @@ Copyright (c) 2017, deadc0de6 yaml config file manager """ -import inspect import itertools import os import shlex @@ -19,7 +18,7 @@ from dotdrop.dotfile import Dotfile from dotdrop.templategen import Templategen from dotdrop.logger import Logger from dotdrop.action import Action, Transform -from dotdrop.utils import is_dict, is_not_magic, strip_home, shell +from dotdrop.utils import strip_home, shell from dotdrop.linktypes import LinkTypes @@ -269,7 +268,8 @@ class Cfg: # parse external actions try: - for path in self.lnk_settings[self.key_import_actions]: + ext_actions = self.lnk_settings[self.key_import_actions] or () + for path in ext_actions: path = self._abs_path(path) if self.debug: self.log.dbg('loading actions from {}'.format(path)) @@ -491,6 +491,27 @@ class Cfg: self.log.dbg('dotfiles for \"{}\": {}'.format(k, df)) return True + def _merge_dict(self, ext_config, warning_prefix, self_member, + ext_member=None): + if ext_member is None: + member_name = self_member + self_member = getattr(self, member_name) + ext_member = getattr(ext_config, member_name) + + common_keys = (set(self_member.keys()) + .intersection(set(ext_member.keys()))) + warning_msg = ('%s {} defined both in %s and %s: {} in %s used' + % (warning_prefix, self.cfgpath, ext_config.cfgpath, + self.cfgpath)) + for key in common_keys: + self.log.warn(warning_msg.format(key, key)) + + merged = ext_member.copy() + merged.update(self_member) + self_member.update(merged) + + return self_member + def _merge_cfg(self, config_path): # Parsing external config file try: @@ -499,35 +520,22 @@ class Cfg: raise ValueError( 'external config file not found: {}'.format(config_path)) - # Merging lists in config settings - list_settings = ( - (k, v) - for k, v in ext_config.lnk_settings.items() - if isinstance(v, list) - ) - for k, v in list_settings: - self.lnk_settings[k] += v + # Merging in members from the external config file + self._merge_dict(ext_config, 'Dotfile', 'dotfiles') + self._merge_dict(ext_config, 'Profile', 'lnk_profiles') + self._merge_dict(ext_config, 'Action', 'actions') + self._merge_dict(ext_config, 'Transformation', 'trans_r') + self._merge_dict(ext_config, 'Write transformation', 'trans_w') + self._merge_dict(ext_config, 'Profile', 'prodots') - # Merging dictionaries - ext_members = ( - (name, member) - for name, member in inspect.getmembers(ext_config, is_dict) - if name != 'content' and is_not_magic(name) - ) - for name, ext_member in ext_members: - self_member = getattr(self, name, {}) - ext_member.update(self_member) - setattr(self, name, ext_member) - - # Merging variables - self_content, ext_content = self.content, ext_config.content - (ext_content[self.key_variables] - .update(self_content[self.key_variables])) - self.content[self.key_variables] = ext_content[self.key_variables] - (ext_content[self.key_dynvariables] - .update(self_content[self.key_dynvariables])) - self.content[self.key_dynvariables] = \ - ext_content[self.key_dynvariables] + # variables need to be merged differently + merged_variables = self._merge_dict(ext_config, 'Variable', + self.get_variables(None), + ext_config.get_variables(None)) + self.content.setdefault(self.key_variables, {}) + self.content[self.key_variables] = self.content[self.key_variables] \ + or {} + self.content[self.key_variables].update(merged_variables) def _load_ext_variables(self, paths, profile=None): """load external variables""" diff --git a/dotdrop/utils.py b/dotdrop/utils.py index e24099f..70b1a61 100644 --- a/dotdrop/utils.py +++ b/dotdrop/utils.py @@ -130,13 +130,3 @@ def must_ignore(paths, ignores, debug=False): LOG.dbg('ignore \"{}\" match: {}'.format(i, p)) return True return False - - -def is_dict(obj): - """Return true if obj is dict.""" - return isinstance(obj, dict) - - -def is_not_magic(name): - """Return true if name is not a magic method name.""" - return (name[0:2], name[-2:]) != ('__', '__') diff --git a/tests/test_config.py b/tests/test_config.py index 85cf1c8..2b23d7f 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -226,10 +226,32 @@ profiles: vars_ed_file = create_yaml_keyval(vars_ed, tmp) vars_ing_file = create_yaml_keyval(vars_ing, tmp) + actions_ed = { + 'pre': { + 'a_pre_action_ed': 'echo pre 22', + }, + 'post': { + 'a_post_action_ed': 'echo post 22', + }, + 'a_action_ed': 'echo 22', + } + actions_ing = { + 'pre': { + 'a_pre_action_ing': 'echo pre aa', + }, + 'post': { + 'a_post_action_ing': 'echo post aa', + }, + 'a_action_ing': 'echo aa', + } + actions_ed_file = create_yaml_keyval(actions_ed, tmp) + actions_ing_file = create_yaml_keyval(actions_ing, tmp) + imported = { 'config': { 'dotpath': 'importing', 'import_variables': [vars_ed_file], + 'import_actions': [actions_ed_file], }, 'dotfiles': { 'f_vimrc': {'dst': '~/.vimrc', 'src': 'vimrc'}, @@ -265,6 +287,7 @@ profiles: 'config': { 'dotpath': 'importing', 'import_variables': [vars_ing_file], + 'import_actions': [actions_ing_file], }, 'dotfiles': { 'f_xinitrc': {'dst': '~/.xinitrc', 'src': 'xinitrc'}, @@ -328,10 +351,6 @@ profiles: self.assertIsNotNone(importing_cfg) self.assertIsNotNone(imported_cfg) - # test settings - self.assertIsSubset(imported_cfg.lnk_settings, - importing_cfg.lnk_settings) - # test profiles self.assertIsSubset(imported_cfg.lnk_profiles, importing_cfg.lnk_profiles) @@ -365,14 +384,6 @@ profiles: # test prodots self.assertIsSubset(imported_cfg.prodots, importing_cfg.prodots) - # test ext_variables (reduntant, but still) - self.assertIsSubset(imported_cfg.ext_variables, - importing_cfg.ext_variables) - - # test ext_dynvariables (reduntant, but still) - self.assertIsSubset(imported_cfg.ext_dynvariables, - importing_cfg.ext_dynvariables) - def test_import_configs_override(self): """Test import_configs when some config keys overlap.""" tmp = get_tempdir() @@ -398,11 +409,33 @@ profiles: vars_ed_file = create_yaml_keyval(vars_ed, tmp) vars_ing_file = create_yaml_keyval(vars_ing, tmp) + actions_ed = { + 'pre': { + 'a_pre_action': 'echo pre 22', + }, + 'post': { + 'a_post_action': 'echo post 22', + }, + 'a_action': 'echo 22', + } + actions_ing = { + 'pre': { + 'a_pre_action': 'echo pre aa', + }, + 'post': { + 'a_post_action': 'echo post aa', + }, + 'a_action': 'echo aa', + } + actions_ed_file = create_yaml_keyval(actions_ed, tmp) + actions_ing_file = create_yaml_keyval(actions_ing, tmp) + imported = { 'config': { 'dotpath': 'imported', 'backup': False, 'import_variables': [vars_ed_file], + 'import_actions': [actions_ed_file], }, 'dotfiles': { 'f_vimrc': {'dst': '~/.vimrc', 'src': 'vimrc'}, @@ -444,6 +477,7 @@ profiles: 'dotpath': 'importing', 'backup': True, 'import_variables': [vars_ing_file], + 'import_actions': [actions_ing_file], }, 'dotfiles': { 'f_xinitrc': {'dst': '~/.xinitrc', 'src': 'xinitrc'}, @@ -507,12 +541,6 @@ profiles: self.assertIsNotNone(importing_cfg) self.assertIsNotNone(imported_cfg) - # test settings - self.assertTrue(importing_cfg.lnk_settings['dotpath'] - .endswith(importing['config']['dotpath'])) - self.assertEqual(importing_cfg.lnk_settings['backup'], - importing['config']['backup']) - # test profiles self.assertIsSubset(imported_cfg.lnk_profiles, importing_cfg.lnk_profiles) @@ -561,19 +589,6 @@ profiles: self.assertTrue(set(imported_cfg.prodots['host1']) < set(importing_cfg.prodots['host2'])) - # test ext_variables (reduntant, but still) - self.assertFalse(any( - imported_cfg.ext_variables[key] == importing_cfg.ext_variables[key] - for key in imported_cfg.ext_variables - )) - - # test ext_dynvariables (reduntant, but still) - self.assertFalse(any( - (imported_cfg.ext_dynvariables[key] - == importing_cfg.ext_dynvariables[key]) - for key in imported_cfg.ext_dynvariables - )) - def main(): unittest.main()