diff --git a/dotdrop/cfg_aggregator.py b/dotdrop/cfg_aggregator.py index 07a4407..fc751d6 100644 --- a/dotdrop/cfg_aggregator.py +++ b/dotdrop/cfg_aggregator.py @@ -253,7 +253,6 @@ class CfgAggregator: def get_dotfile_by_dst(self, dst): """get a dotfile by dst""" - dst = self.path_to_dotfile_dst(dst) try: return next(d for d in self.dotfiles if d.dst == dst) except StopIteration: diff --git a/dotdrop/cfg_yaml.py b/dotdrop/cfg_yaml.py index 6b69d52..b98b48c 100644 --- a/dotdrop/cfg_yaml.py +++ b/dotdrop/cfg_yaml.py @@ -16,6 +16,7 @@ from dotdrop.logger import Logger from dotdrop.templategen import Templategen from dotdrop.linktypes import LinkTypes from dotdrop.utils import shell, uniq_list +from dotdrop.exceptions import YamlException class CfgYaml: @@ -116,7 +117,8 @@ class CfgYaml: keys = self.dotfiles.keys() if len(keys) != len(list(set(keys))): dups = [x for x in keys if x not in list(set(keys))] - raise Exception('duplicate dotfile keys found: {}'.format(dups)) + err = 'duplicate dotfile keys found: {}'.format(dups) + raise YamlException(err) self.dotfiles = self._norm_dotfiles(self.dotfiles) if self.debug: self.log.dbg('dotfiles: {}'.format(self.dotfiles)) @@ -329,7 +331,7 @@ class CfgYaml: # inherite profile variables for inherited_profile in pentry.get(self.key_profile_include, []): if inherited_profile == profile or inherited_profile in seen: - raise Exception('\"include\" loop') + raise YamlException('\"include\" loop') seen.append(inherited_profile) new = self._get_variables_dict(inherited_profile, seen, sub=True) variables.update(new) @@ -356,7 +358,7 @@ class CfgYaml: # inherite profile dynvariables for inherited_profile in pentry.get(self.key_profile_include, []): if inherited_profile == profile or inherited_profile in seen: - raise Exception('\"include loop\"') + raise YamlException('\"include loop\"') seen.append(inherited_profile) new = self._get_dvariables_dict(inherited_profile, seen, sub=True) variables.update(new) @@ -383,7 +385,7 @@ class CfgYaml: p = os.path.expanduser(p) new = glob.glob(p) if not new: - raise Exception('bad path: {}'.format(p)) + raise YamlException('bad path: {}'.format(p)) res.extend(glob.glob(p)) return res @@ -438,8 +440,7 @@ class CfgYaml: current = v.get(self.key_dotfiles, []) path = self._resolve_path(p) current = self._import_sub(path, self.key_dotfiles, - current, mandatory=False, - path_func=self._norm_dotfiles) + current, mandatory=False) v[self.key_dotfiles] = current def _import_configs(self): @@ -491,7 +492,7 @@ class CfgYaml: seen = [] for i in inc: if i in seen: - raise Exception('\"include loop\"') + raise YamlException('\"include loop\"') seen.append(i) if i not in self.profiles.keys(): self.log.warn('include unknown profile: {}'.format(i)) @@ -541,7 +542,7 @@ class CfgYaml: elif isinstance(current, list) and isinstance(new, list): current = current + new else: - raise Exception('invalid import {} from {}'.format(key, path)) + raise YamlException('invalid import {} from {}'.format(key, path)) if self.debug: self.log.dbg('new \"{}\": {}'.format(key, current)) return current @@ -558,7 +559,7 @@ class CfgYaml: """return entry from yaml dictionary""" if key not in dic: if mandatory: - raise Exception('invalid config: no {} found'.format(key)) + raise YamlException('invalid config: no {} found'.format(key)) dic[key] = {} return dic[key] if mandatory and not dic[key]: @@ -570,13 +571,13 @@ class CfgYaml: """load a yaml file to a dict""" content = {} if not os.path.exists(path): - raise Exception('config path not found: {}'.format(path)) + raise YamlException('config path not found: {}'.format(path)) with open(path, 'r') as f: try: content = yaml.safe_load(f) except Exception as e: self.log.err(e) - raise Exception('invalid config: {}'.format(path)) + raise YamlException('invalid config: {}'.format(path)) return content def _new_profile(self, key): @@ -718,8 +719,13 @@ class CfgYaml: if isinstance(v, dict): newv = self._clear_none(v) if not newv: + # no empty dict continue if newv is None: + # no None value + continue + if isinstance(newv, list) and not newv: + # no empty list continue new[k] = newv return new @@ -730,12 +736,27 @@ class CfgYaml: return False content = self._clear_none(self.dump()) + + # make sure we have the base entries + if self.key_settings not in content: + content[self.key_settings] = None + if self.key_dotfiles not in content: + content[self.key_dotfiles] = None + if self.key_profiles not in content: + content[self.key_profiles] = None + + # ensure no null are displayed + data = yaml.safe_dump(content, + default_flow_style=False, + indent=2) + data = data.replace('null', '') + + # save to file if self.debug: self.log.dbg('saving: {}'.format(content)) with open(self.path, 'w') as f: - yaml.safe_dump(content, f, - default_flow_style=False, - indent=2) + f.write(data) + self.dirty = False return True diff --git a/dotdrop/dotdrop.py b/dotdrop/dotdrop.py index 1721d46..7f49186 100644 --- a/dotdrop/dotdrop.py +++ b/dotdrop/dotdrop.py @@ -17,6 +17,7 @@ from dotdrop.updater import Updater from dotdrop.comparator import Comparator from dotdrop.utils import get_tmpdir, remove, strip_home, run, uniq_list from dotdrop.linktypes import LinkTypes +from dotdrop.exceptions import YamlException LOG = Logger() TRANS_SUFFIX = 'trans' @@ -423,7 +424,9 @@ def cmd_remove(o): LOG.dbg('removing {}'.format(key)) if not iskey: # by path + print(key) dotfile = o.conf.get_dotfile_by_dst(key) + print(dotfile) if not dotfile: LOG.warn('{} ignored, does not exist'.format(key)) continue @@ -441,7 +444,7 @@ def cmd_remove(o): if o.dry: LOG.dry('would remove {} from {}'.format(dotfile, pkeys)) continue - msg = 'Remove dotfile from all these profiles: {}'.format(pkeys) + msg = 'Remove \"{}\" from all these profiles: {}'.format(k, pkeys) if o.safe and not LOG.ask(msg): return False if o.debug: @@ -537,8 +540,8 @@ def main(): """entry point""" try: o = Options() - except Exception as e: - LOG.err('options error: {}'.format(str(e))) + except YamlException as e: + LOG.err('config file error: {}'.format(str(e))) return False ret = True diff --git a/dotdrop/exceptions.py b/dotdrop/exceptions.py new file mode 100644 index 0000000..a327696 --- /dev/null +++ b/dotdrop/exceptions.py @@ -0,0 +1,11 @@ +""" +author: deadc0de6 (https://github.com/deadc0de6) +Copyright (c) 2019, deadc0de6 + +diverse exceptions +""" + + +class YamlException(Exception): + """exception in CfgYaml""" + pass diff --git a/dotdrop/profile.py b/dotdrop/profile.py index c1147ee..fd7bd3f 100644 --- a/dotdrop/profile.py +++ b/dotdrop/profile.py @@ -15,18 +15,21 @@ class Profile(DictParser): key_include = 'include' key_import = 'import' - def __init__(self, key, actions=[], dotfiles=[], variables=[]): + def __init__(self, key, actions=[], dotfiles=[], + variables=[], dynvariables=[]): """ constructor @key: profile key @actions: list of action keys @dotfiles: list of dotfile keys @variables: list of variable keys + @dynvariables: list of interpreted variable keys """ self.key = key self.actions = actions self.dotfiles = dotfiles self.variables = variables + self.dynvariables = dynvariables def get_pre_actions(self): """return all 'pre' actions"""