diff --git a/dotdrop/action.py b/dotdrop/action.py index ccac7ba..ccc7bf5 100644 --- a/dotdrop/action.py +++ b/dotdrop/action.py @@ -34,15 +34,19 @@ class Cmd(DictParser): if templater: action = templater.generate_string(self.action) if debug: - self.log.dbg('{} \"{}\" -> \"{}\"'.format(self.descr, - self.action, - action)) + self.log.dbg('{}:'.format(self.descr)) + self.log.dbg(' - raw \"{}\"'.format(self.action)) + self.log.dbg(' - templated \"{}\"'.format(action)) cmd = action args = [] if self.args: args = self.args if templater: args = [templater.generate_string(a) for a in args] + if debug and args: + self.log.dbg('action args:') + for cnt, arg in enumerate(args): + self.log.dbg('\targs[{}]: {}'.format(cnt, arg)) try: cmd = action.format(*args) except IndexError: @@ -57,7 +61,11 @@ class Cmd(DictParser): return False if self.silent: self.log.sub('executing silent action \"{}\"'.format(self.key)) + if debug: + self.log.dbg('action cmd silenced') else: + if debug: + self.log.dbg('action cmd: \"{}\"'.format(cmd)) self.log.sub('executing \"{}\"'.format(cmd)) try: ret = subprocess.call(cmd, shell=True) @@ -124,8 +132,8 @@ class Action(Cmd): return cls(key=key, **v) def __str__(self): - out = '{}: \"{}\" ({})' - return out.format(self.key, self.action, self.kind) + out = '{}: [{}] \"{}\"' + return out.format(self.key, self.kind, self.action) def __repr__(self): return 'action({})'.format(self.__str__()) diff --git a/dotdrop/cfg_aggregator.py b/dotdrop/cfg_aggregator.py index 61f877d..8360ff4 100644 --- a/dotdrop/cfg_aggregator.py +++ b/dotdrop/cfg_aggregator.py @@ -50,38 +50,36 @@ class CfgAggregator: # settings self.settings = Settings.parse(None, self.cfgyaml.settings) - if self.debug: - self.log.dbg('settings: {}'.format(self.settings)) # dotfiles self.dotfiles = Dotfile.parse_dict(self.cfgyaml.dotfiles) if self.debug: - self.log.dbg('dotfiles: {}'.format(self.dotfiles)) + self._debug_list('dotfiles', self.dotfiles) # profiles self.profiles = Profile.parse_dict(self.cfgyaml.profiles) if self.debug: - self.log.dbg('profiles: {}'.format(self.profiles)) + self._debug_list('profiles', self.profiles) # actions self.actions = Action.parse_dict(self.cfgyaml.actions) if self.debug: - self.log.dbg('actions: {}'.format(self.actions)) + self._debug_list('actions', self.actions) # trans_r self.trans_r = Transform.parse_dict(self.cfgyaml.trans_r) if self.debug: - self.log.dbg('trans_r: {}'.format(self.trans_r)) + self._debug_list('trans_r', self.trans_r) # trans_w self.trans_w = Transform.parse_dict(self.cfgyaml.trans_w) if self.debug: - self.log.dbg('trans_w: {}'.format(self.trans_w)) + self._debug_list('trans_w', self.trans_w) # variables self.variables = self.cfgyaml.get_variables() if self.debug: - self.log.dbg('variables: {}'.format(self.variables)) + self._debug_dict('variables', self.variables) # patch dotfiles in profiles self._patch_keys_to_objs(self.profiles, @@ -403,3 +401,19 @@ class CfgAggregator: path = os.path.expandvars(path) path = os.path.abspath(path) return path + + def _debug_list(self, title, elems): + """pretty print list""" + if not self.debug: + return + self.log.dbg('{}:'.format(title)) + for e in elems: + self.log.dbg('\t- {}'.format(e)) + + def _debug_dict(self, title, elems): + """pretty print dict""" + if not self.debug: + return + self.log.dbg('{}:'.format(title)) + for k, v in elems.items(): + self.log.dbg('\t- \"{}\": {}'.format(k, v)) diff --git a/dotdrop/cfg_yaml.py b/dotdrop/cfg_yaml.py index e66adde..c254dc2 100644 --- a/dotdrop/cfg_yaml.py +++ b/dotdrop/cfg_yaml.py @@ -104,7 +104,7 @@ class CfgYaml: # parse to self variables self._parse_main_yaml(self.yaml_dict) if self.debug: - self.log.dbg('before normalization: {}'.format(self.yaml_dict)) + self.log.dbg('BEFORE normalization: {}'.format(self.yaml_dict)) # resolve variables self.variables, self.prokeys = self._merge_variables() @@ -129,7 +129,7 @@ class CfgYaml: self._resolve_dotfile_paths() if self.debug: - self.log.dbg('after normalization: {}'.format(self.yaml_dict)) + self.log.dbg('AFTER normalization: {}'.format(self.yaml_dict)) def get_variables(self): """retrieve all variables""" @@ -166,7 +166,7 @@ class CfgYaml: ] self.settings[Settings.key_func_file] = p if self.debug: - self.log.dbg('settings: {}'.format(self.settings)) + self._debug_dict('settings', self.settings) # dotfiles self.ori_dotfiles = self._get_entry(dic, self.key_dotfiles) @@ -178,14 +178,14 @@ class CfgYaml: raise YamlException(err) self.dotfiles = self._norm_dotfiles(self.dotfiles) if self.debug: - self.log.dbg('dotfiles: {}'.format(self.dotfiles)) + self._debug_dict('dotfiles', self.dotfiles) # profiles self.ori_profiles = self._get_entry(dic, self.key_profiles) self.profiles = deepcopy(self.ori_profiles) self.profiles = self._norm_profiles(self.profiles) if self.debug: - self.log.dbg('profiles: {}'.format(self.profiles)) + self._debug_dict('profiles', self.profiles) # actions self.ori_actions = self._get_entry(dic, self.key_actions, @@ -193,7 +193,7 @@ class CfgYaml: self.actions = deepcopy(self.ori_actions) self.actions = self._norm_actions(self.actions) if self.debug: - self.log.dbg('actions: {}'.format(self.actions)) + self._debug_dict('actions', self.actions) # trans_r key = self.key_trans_r @@ -204,28 +204,28 @@ class CfgYaml: self.ori_trans_r = self._get_entry(dic, key, mandatory=False) self.trans_r = deepcopy(self.ori_trans_r) if self.debug: - self.log.dbg('trans_r: {}'.format(self.trans_r)) + self._debug_dict('trans_r', self.trans_r) # trans_w self.ori_trans_w = self._get_entry(dic, self.key_trans_w, mandatory=False) self.trans_w = deepcopy(self.ori_trans_w) if self.debug: - self.log.dbg('trans_w: {}'.format(self.trans_w)) + self._debug_dict('trans_w', self.trans_w) # variables self.ori_variables = self._get_entry(dic, self.key_variables, mandatory=False) if self.debug: - self.log.dbg('variables: {}'.format(self.ori_variables)) + self._debug_dict('variables', self.ori_variables) # dynvariables self.ori_dvariables = self._get_entry(dic, self.key_dvariables, mandatory=False) if self.debug: - self.log.dbg('dynvariables: {}'.format(self.ori_dvariables)) + self._debug_dict('dynvariables', self.ori_dvariables) def _resolve_dotfile_paths(self): """resolve dotfiles paths""" @@ -305,13 +305,14 @@ class CfgYaml: # temporarly resolve all variables for "include" merged = self._merge_dict(dvar, var) merged = self._rec_resolve_vars(merged) - self._debug_vars(merged) + if self.debug: + self._debug_dict('variables', merged) # exec dynvariables self._shell_exec_dvars(dvar.keys(), merged) if self.debug: self.log.dbg('local variables resolved') - self._debug_vars(merged) + self._debug_dict('variables', merged) # resolve profile includes t = Templategen(variables=merged, @@ -340,7 +341,7 @@ class CfgYaml: if self.debug: self.log.dbg('resolve all uses of variables in config') - self._debug_vars(merged) + self._debug_dict('variables', merged) prokeys = list(pro_var.keys()) + list(pro_dvar.keys()) return merged, prokeys @@ -751,7 +752,7 @@ class CfgYaml: self._clear_profile_vars(sub.variables) if self.debug: - self.log.dbg('add import_configs var: {}'.format(sub.variables)) + self._debug_dict('add import_configs var', sub.variables) self.variables = self._merge_dict(sub.variables, self.variables) def _import_configs(self): @@ -997,6 +998,14 @@ class CfgYaml: def _load_yaml(self, path): """load a yaml file to a dict""" content = {} + if self.debug: + self.log.dbg('----------start:{}----------'.format(path)) + cfg = '\n' + with open(path, 'r') as f: + for line in f: + cfg += line + self.log.dbg(cfg.rstrip()) + self.log.dbg('----------end:{}----------'.format(path)) try: content = self._yaml_load(path) except Exception as e: @@ -1074,29 +1083,22 @@ class CfgYaml: expanded_path = os.path.expanduser(path) return glob.glob(expanded_path, recursive=True) - def _debug_vars(self, variables): - """pretty print variables""" - if not self.debug: - return - self.log.dbg('variables:') - for k, v in variables.items(): - self.log.dbg('\t\"{}\": {}'.format(k, v)) - def _norm_path(self, path): """Resolve a path either absolute or relative to config path""" - if self.debug: - self.log.dbg('normalizing path {}'.format(path)) if not path: return path path = os.path.expanduser(path) if not os.path.isabs(path): - if self.debug: - self.log.dbg('normalizing path {} relative to config file ' - 'directory'.format(path)) - d = os.path.dirname(self.path) - return os.path.join(d, path) - return os.path.normpath(path) + ret = os.path.join(d, path) + if self.debug: + msg = 'normalizing relative to cfg: {} -> {}' + self.log.dbg(msg.format(path, ret)) + return ret + ret = os.path.normpath(path) + if self.debug and path != ret: + self.log.dbg('normalizing: {} -> {}'.format(path, ret)) + return ret def _shell_exec_dvars(self, keys, variables): """shell execute dynvariables""" @@ -1135,3 +1137,13 @@ class CfgYaml: err = 'current dotdrop version is too old for that config file.' err += ' Please update.' raise YamlException(err) + + def _debug_dict(self, title, elems): + """pretty print dict""" + if not self.debug: + return + self.log.dbg('{}:'.format(title)) + if not elems: + return + for k, v in elems.items(): + self.log.dbg('\t- \"{}\": {}'.format(k, v)) diff --git a/dotdrop/dotdrop.py b/dotdrop/dotdrop.py index 3af210f..417c095 100644 --- a/dotdrop/dotdrop.py +++ b/dotdrop/dotdrop.py @@ -47,7 +47,7 @@ def action_executor(o, actions, defactions, templater, post=False): action)) continue if o.debug: - LOG.dbg('executing def-{}-action {}'.format(s, action)) + LOG.dbg('executing def-{}-action: {}'.format(s, action)) ret = action.execute(templater=templater, debug=o.debug) if not ret: err = 'def-{}-action \"{}\" failed'.format(s, action.key) @@ -60,7 +60,7 @@ def action_executor(o, actions, defactions, templater, post=False): LOG.dry('would execute {}-action: {}'.format(s, action)) continue if o.debug: - LOG.dbg('executing {}-action {}'.format(s, action)) + LOG.dbg('executing {}-action: {}'.format(s, action)) ret = action.execute(templater=templater, debug=o.debug) if not ret: err = '{}-action \"{}\" failed'.format(s, action.key) @@ -105,7 +105,7 @@ def cmd_install(o): # execute profile pre-action if o.debug: - LOG.dbg('execute profile pre actions') + LOG.dbg('run {} profile pre actions'.format(len(pro_pre_actions))) ret, err = action_executor(o, pro_pre_actions, [], t, post=False)() if not ret: return False @@ -125,7 +125,8 @@ def cmd_install(o): t, post=False) if o.debug: - LOG.dbg('installing {}'.format(dotfile)) + LOG.dbg('installing dotfile: \"{}\"'.format(dotfile.key)) + LOG.dbg(dotfile.prt()) if hasattr(dotfile, 'link') and dotfile.link == LinkTypes.LINK: r, err = inst.link(t, dotfile.src, dotfile.dst, actionexec=pre_actions_exec) @@ -181,11 +182,15 @@ def cmd_install(o): # execute profile post-action if installed > 0 or o.install_force_action: if o.debug: - LOG.dbg('execute profile post actions') + msg = 'run {} profile post actions' + LOG.dbg(msg.format(len(pro_post_actions))) ret, err = action_executor(o, pro_post_actions, [], t, post=False)() if not ret: return False + if o.debug: + LOG.dbg('install done') + if o.install_temporary: LOG.log('\ninstalled to tmp \"{}\".'.format(tmpdir)) LOG.log('\n{} dotfile(s) installed.'.format(installed)) @@ -628,7 +633,7 @@ def apply_trans(dotpath, dotfile, templater, debug=False): new_src = '{}.{}'.format(src, TRANS_SUFFIX) trans = dotfile.trans_r if debug: - LOG.dbg('executing transformation {}'.format(trans)) + LOG.dbg('executing transformation: {}'.format(trans)) s = os.path.join(dotpath, src) temp = os.path.join(dotpath, new_src) if not trans.transform(s, temp, templater=templater, debug=debug): diff --git a/dotdrop/dotfile.py b/dotdrop/dotfile.py index 8a61cae..ae2025c 100644 --- a/dotdrop/dotfile.py +++ b/dotdrop/dotfile.py @@ -107,5 +107,36 @@ class Dotfile(DictParser): msg = 'key:\"{}\", src:\"{}\", dst:\"{}\", link:\"{}\"' return msg.format(self.key, self.src, self.dst, str(self.link)) + def prt(self): + """extended dotfile to str""" + indent = ' ' + out = 'dotfile: \"{}\"'.format(self.key) + out += '\n{}src: \"{}\"'.format(indent, self.src) + out += '\n{}dst: \"{}\"'.format(indent, self.dst) + out += '\n{}link: \"{}\"'.format(indent, str(self.link)) + + out += '\n{}pre-action:'.format(indent) + some = self.get_pre_actions() + if some: + for a in some: + out += '\n{}- {}'.format(2 * indent, a) + + out += '\n{}post-action:'.format(indent) + some = self.get_post_actions() + if some: + for a in some: + out += '\n{}- {}'.format(2 * indent, a) + + out += '\n{}trans_r:'.format(indent) + some = self.get_trans_r() + if some: + out += '\n{}- {}'.format(2 * indent, some) + + out += '\n{}trans_w:'.format(indent) + some = self.get_trans_w() + if some: + out += '\n{}- {}'.format(2 * indent, some) + return out + def __repr__(self): return 'dotfile({!s})'.format(self) diff --git a/dotdrop/installer.py b/dotdrop/installer.py index 46cedd2..0cc6856 100644 --- a/dotdrop/installer.py +++ b/dotdrop/installer.py @@ -50,6 +50,18 @@ class Installer: self.action_executed = False self.log = Logger() + def _log_install(self, boolean, err): + if not self.debug: + return boolean, err + if boolean: + self.log.dbg('install: SUCCESS') + else: + if err: + self.log.dbg('install: ERROR: {}'.format(err)) + else: + self.log.dbg('install: IGNORED') + return boolean, err + def install(self, templater, src, dst, actionexec=None, noempty=False, ignore=[]): @@ -68,34 +80,36 @@ class Installer: - False, None : ignored """ if self.debug: - self.log.dbg('install \"{}\" to \"{}\"'.format(src, dst)) + self.log.dbg('installing \"{}\" to \"{}\"'.format(src, dst)) if not dst or not src: if self.debug: self.log.dbg('empty dst for {}'.format(src)) - return True, None + return self._log_install(True, None) self.action_executed = False src = os.path.join(self.base, os.path.expanduser(src)) if not os.path.exists(src): err = 'source dotfile does not exist: {}'.format(src) - return False, err + return self._log_install(False, err) dst = os.path.expanduser(dst) if self.totemp: dst = self._pivot_path(dst, self.totemp) if utils.samefile(src, dst): # symlink loop err = 'dotfile points to itself: {}'.format(dst) - return False, err + return self._log_install(False, err) isdir = os.path.isdir(src) if self.debug: self.log.dbg('install {} to {}'.format(src, dst)) - self.log.dbg('is \"{}\" a directory: {}'.format(src, isdir)) + self.log.dbg('is a directory \"{}\": {}'.format(src, isdir)) if isdir: - return self._handle_dir(templater, src, dst, + b, e = self._handle_dir(templater, src, dst, actionexec=actionexec, noempty=noempty, ignore=ignore) - return self._handle_file(templater, src, dst, + return self._log_install(b, e) + b, e = self._handle_file(templater, src, dst, actionexec=actionexec, noempty=noempty, ignore=ignore) + return self._log_install(b, e) def link(self, templater, src, dst, actionexec=None): """ @@ -115,17 +129,18 @@ class Installer: if not dst or not src: if self.debug: self.log.dbg('empty dst for {}'.format(src)) - return True, None + return self._log_install(True, None) self.action_executed = False src = os.path.normpath(os.path.join(self.base, os.path.expanduser(src))) if not os.path.exists(src): err = 'source dotfile does not exist: {}'.format(src) - return False, err + return self._log_install(False, err) dst = os.path.normpath(os.path.expanduser(dst)) if self.totemp: # ignore actions - return self.install(templater, src, dst, actionexec=None) + b, e = self.install(templater, src, dst, actionexec=None) + return self._log_install(b, e) if Templategen.is_template(src): if self.debug: @@ -134,9 +149,10 @@ class Installer: tmp = self._pivot_path(dst, self.workdir, striphome=True) i, err = self.install(templater, src, tmp, actionexec=actionexec) if not i and not os.path.exists(tmp): - return i, err + return self._log_install(i, err) src = tmp - return self._link(src, dst, actionexec=actionexec) + b, e = self._link(src, dst, actionexec=actionexec) + return self._log_install(b, e) def link_children(self, templater, src, dst, actionexec=None): """ @@ -156,14 +172,14 @@ class Installer: if not dst or not src: if self.debug: self.log.dbg('empty dst for {}'.format(src)) - return True, None + return self._log_install(True, None) self.action_executed = False parent = os.path.join(self.base, os.path.expanduser(src)) # Fail if source doesn't exist if not os.path.exists(parent): err = 'source dotfile does not exist: {}'.format(parent) - return False, err + return self._log_install(False, err) # Fail if source not a directory if not os.path.isdir(parent): @@ -171,7 +187,7 @@ class Installer: self.log.dbg('symlink children of {} to {}'.format(src, dst)) err = 'source dotfile is not a directory: {}'.format(parent) - return False, err + return self._log_install(False, err) dst = os.path.normpath(os.path.expanduser(dst)) if not os.path.lexists(dst): @@ -186,7 +202,7 @@ class Installer: if self.safe and not self.log.ask(msg): err = 'ignoring "{}", nothing installed'.format(dst) - return False, err + return self._log_install(False, err) os.unlink(dst) os.mkdir(dst) @@ -224,8 +240,9 @@ class Installer: else: if err: return ret, err + return self._log_install(ret, err) - return installed > 0, None + return self._log_install(installed > 0, None) def _link(self, src, dst, actionexec=None): """ @@ -429,7 +446,7 @@ class Installer: if not r: return -1, e if self.debug: - self.log.dbg('write content to {}'.format(dst)) + self.log.dbg('writes content to \"{}\"'.format(dst)) # re-check in case action created the file if self.safe and not overwrite and os.path.lexists(dst): if not self.log.ask('Overwrite \"{}\"'.format(dst)): diff --git a/dotdrop/options.py b/dotdrop/options.py index 3760e71..d36025f 100644 --- a/dotdrop/options.py +++ b/dotdrop/options.py @@ -184,9 +184,8 @@ class Options(AttrMonitor): self.conf = Cfg(self.confpath, self.profile, debug=self.debug, dry=self.dry) # transform the config settings to self attribute + self._debug_dict('effective settings', self.conf.get_settings()) for k, v in self.conf.get_settings().items(): - if self.debug: - self.log.dbg('new setting: {}={}'.format(k, v)) setattr(self, k, v) def _apply_args(self): @@ -267,15 +266,41 @@ class Options(AttrMonitor): """debug display all of this class attributes""" if not self.debug: return - self.log.dbg('CLI options:') + self.log.dbg('effective options:') for att in dir(self): if att.startswith('_'): continue val = getattr(self, att) if callable(val): continue - self.log.dbg('- {}: {}'.format(att, val)) + if type(val) is list: + self._debug_list('-> {}'.format(att), val) + elif type(val) is dict: + self._debug_dict('-> {}'.format(att), val) + else: + self.log.dbg('-> {}: {}'.format(att, val)) def _attr_set(self, attr): """error when some inexistent attr is set""" raise Exception('bad option: {}'.format(attr)) + + def _debug_list(self, title, elems): + """pretty print list""" + if not self.debug: + return + self.log.dbg('{}:'.format(title)) + for e in elems: + self.log.dbg('\t- {}'.format(e)) + + def _debug_dict(self, title, elems): + """pretty print dict""" + if not self.debug: + return + self.log.dbg('{}:'.format(title)) + for k, v in elems.items(): + if type(v) is list: + self.log.dbg('\t- \"{}\":'.format(k)) + for i in v: + self.log.dbg('\t\t- {}'.format(i)) + else: + self.log.dbg('\t- \"{}\": {}'.format(k, v)) diff --git a/dotdrop/templategen.py b/dotdrop/templategen.py index 1c97eec..54aa90c 100644 --- a/dotdrop/templategen.py +++ b/dotdrop/templategen.py @@ -69,7 +69,7 @@ class Templategen: self.log.dbg('load custom filters from {}'.format(f)) self._load_path_to_dic(f, self.env.filters) if self.debug: - self.log.dbg('template additional variables: {}'.format(variables)) + self._debug_dict('template additional variables', variables) def generate(self, src): """render template from path""" @@ -126,10 +126,10 @@ class Templategen: raw=False, debug=self.debug) filetype = filetype.strip() if self.debug: - self.log.dbg('\"{}\" filetype: {}'.format(src, filetype)) + self.log.dbg('filetype \"{}\": {}'.format(src, filetype)) istext = self._is_text(filetype) if self.debug: - self.log.dbg('\"{}\" is text: {}'.format(src, istext)) + self.log.dbg('is text \"{}\": {}'.format(src, istext)) if not istext: return self._handle_bin_file(src) return self._handle_text_file(src) @@ -223,3 +223,13 @@ class Templategen: if marker in data: return True return False + + def _debug_dict(self, title, elems): + """pretty print dict""" + if not self.debug: + return + self.log.dbg('{}:'.format(title)) + if not elems: + return + for k, v in elems.items(): + self.log.dbg(' - \"{}\": {}'.format(k, v))