From 5b9d24821924192ee5e052603a40cc3e35a1fbe9 Mon Sep 17 00:00:00 2001 From: deadc0de6 Date: Thu, 25 Aug 2022 23:21:11 +0200 Subject: [PATCH] linting --- dotdrop/action.py | 36 +++--- dotdrop/cfg_aggregator.py | 10 +- dotdrop/cfg_yaml.py | 224 +++++++++++++++++++------------------- dotdrop/comparator.py | 76 +++++-------- dotdrop/dotfile.py | 47 ++++---- dotdrop/importer.py | 42 +++---- dotdrop/linktypes.py | 2 +- dotdrop/logger.py | 34 +++--- dotdrop/options.py | 43 ++++---- dotdrop/profile.py | 5 +- dotdrop/settings.py | 2 +- dotdrop/templategen.py | 24 ++-- dotdrop/utils.py | 76 ++++++------- tests.sh | 14 ++- 14 files changed, 312 insertions(+), 323 deletions(-) diff --git a/dotdrop/action.py b/dotdrop/action.py index bed113d..d0b7628 100644 --- a/dotdrop/action.py +++ b/dotdrop/action.py @@ -36,13 +36,13 @@ class Cmd(DictParser): try: action = templater.generate_string(self.action) except UndefinedException as exc: - err = 'undefined variable for {}: \"{}\"'.format(self.descr, exc) + err = f'undefined variable for {self.descr}: \"{exc}\"' self.log.warn(err) return False if debug: - self.log.dbg('{}:'.format(self.descr)) - self.log.dbg(' - raw \"{}\"'.format(self.action)) - self.log.dbg(' - templated \"{}\"'.format(action)) + self.log.dbg(f'{self.descr}:') + self.log.dbg(f' - raw \"{self.action}\"') + self.log.dbg(f' - templated \"{action}\"') return action def _get_args(self, templater): @@ -69,36 +69,34 @@ class Cmd(DictParser): if debug and args: self.log.dbg('action args:') for cnt, arg in enumerate(args): - self.log.dbg('\targs[{}]: {}'.format(cnt, arg)) + self.log.dbg(f'\targs[{cnt}]: {arg}') try: cmd = action.format(*args) except IndexError as exc: - err = 'index error for {}: \"{}\"'.format(self.descr, action) - err += ' with \"{}\"'.format(args) - err += ': {}'.format(exc) + err = 'findex error for {self.descr}: \"{action}\"' + err += f' with \"{args}\"' + err += f': {exc}' self.log.warn(err) return False except KeyError as exc: - err = 'key error for {}: \"{}\": {}'.format(self.descr, - action, - exc) - err += ' with \"{}\"'.format(args) + err = f'key error for {self.descr}: \"{action}\": {exc}' + err += ' with \"{args}\"' self.log.warn(err) return False if self.silent: - self.log.sub('executing silent action \"{}\"'.format(self.key)) + self.log.sub(f'executing silent action \"{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)) + self.log.dbg(f'action cmd: \"{cmd}\"') + self.log.sub(f'executing \"{cmd}\"') try: ret = subprocess.call(cmd, shell=True) except KeyboardInterrupt: - self.log.warn('{} interrupted'.format(self.descr)) + self.log.warn('f{self.descr} interrupted') if ret != 0: - self.log.warn('{} returned code {}'.format(self.descr, ret)) + self.log.warn(f'{self.descr} returned code {ret}') return ret == 0 @classmethod @@ -106,7 +104,7 @@ class Cmd(DictParser): return {'action': value} def __str__(self): - return 'key:{} -> \"{}\"'.format(self.key, self.action) + return 'key:{self.key} -> \"{self.action}\"' class Action(Cmd): @@ -144,7 +142,7 @@ class Action(Cmd): return out.format(self.key, self.kind, self.action) def __repr__(self): - return 'action({})'.format(self.__str__()) + return f'action({self.__str__()})' class Transform(Cmd): diff --git a/dotdrop/cfg_aggregator.py b/dotdrop/cfg_aggregator.py index 01f1125..c75513f 100644 --- a/dotdrop/cfg_aggregator.py +++ b/dotdrop/cfg_aggregator.py @@ -221,7 +221,7 @@ class CfgAggregator: """create a new dotfile""" # get a new dotfile with a unique key key = self._get_new_dotfile_key(dst) - self.log.dbg('new dotfile key: {}'.format(key)) + self.log.dbg(f'new dotfile key: {key}') # add the dotfile trans_r_key = trans_w_key = None if trans_read: @@ -298,7 +298,7 @@ class CfgAggregator: self._patch_keys_to_objs([self.settings], "default_actions", self._get_action_w_args) - msg = 'default actions: {}'.format(self.settings.default_actions) + msg = f'default actions: {self.settings.default_actions}' self.log.dbg(msg) # patch trans_w/trans_r in dotfiles @@ -318,7 +318,7 @@ class CfgAggregator: """ if not containers: return - self.log.dbg('patching {} ...'.format(keys)) + self.log.dbg(f'patching {keys} ...') for container in containers: objects = [] okeys = getattr(container, keys) @@ -329,8 +329,8 @@ class CfgAggregator: for key in okeys: obj = get_by_key(key) if not obj: - err = '{} does not contain'.format(container) - err += ' a {} entry named {}'.format(keys, key) + err = f'{container} does not contain' + err += f' a {keys} entry named {key}' self.log.err(err) raise Exception(err) objects.append(obj) diff --git a/dotdrop/cfg_yaml.py b/dotdrop/cfg_yaml.py index 456f2aa..18fab17 100644 --- a/dotdrop/cfg_yaml.py +++ b/dotdrop/cfg_yaml.py @@ -144,15 +144,16 @@ class CfgYaml: self.variables = {} if not os.path.exists(self._path): - err = 'invalid config path: \"{}\"'.format(path) + err = f'invalid config path: \"{path}\"' if self._debug: self._dbg(err) raise YamlException(err) self._dbg('START of config parsing') - self._dbg('reloading: {}'.format(reloading)) - self._dbg('profile: {}'.format(profile)) - self._dbg('included profiles: {}'.format(','.join(self._inc_profiles))) + self._dbg(f'reloading: {reloading}') + self._dbg(f'profile: {profile}') + pfs = ','.join(self._inc_profiles) + self._dbg(f'included profiles: {pfs}') self._yaml_dict = self._load_yaml(self._path) # live patch deprecated entries @@ -287,7 +288,7 @@ class CfgYaml: # end of parsing if self._debug: - self._dbg('########### {} ###########'.format('final config')) + self._dbg('########### final config ###########') self._debug_entries() self._dbg('END of config parsing') @@ -300,10 +301,10 @@ class CfgYaml: newlink = self._template_item(link) # check link value if newlink not in self.allowed_link_val: - err = 'bad link value: {}'.format(newlink) + err = f'bad link value: {newlink}' self._log.err(err) - self._log.err('allowed: {}'.format(self.allowed_link_val)) - raise YamlException('config content error: {}'.format(err)) + self._log.err(f'allowed: {self.allowed_link_val}') + raise YamlException(f'config content error: {err}') return newlink def resolve_dotfile_src(self, src, templater=None): @@ -314,7 +315,7 @@ class CfgYaml: if templater: new = templater.generate_string(src) if new != src and self._debug: - msg = 'dotfile src: \"{}\" -> \"{}\"'.format(src, new) + msg = f'dotfile src: \"{src}\" -> \"{new}\"' self._dbg(msg) src = new src = os.path.join(self.settings[self.key_settings_dotpath], @@ -330,7 +331,7 @@ class CfgYaml: if templater: new = templater.generate_string(dst) if new != dst and self._debug: - msg = 'dotfile dst: \"{}\" -> \"{}\"'.format(dst, new) + msg = f'dotfile dst: \"{dst}\" -> \"{new}\"' self._dbg(msg) dst = new newdst = self._norm_path(dst) @@ -361,8 +362,7 @@ class CfgYaml: pro = self._yaml_dict[self.key_profiles][profile_key] pro[self.key_profile_dotfiles].append(dotfile_key) if self._debug: - msg = 'add \"{}\" to profile \"{}\"'.format(dotfile_key, - profile_key) + msg = f'add \"{dotfile_key}\" to profile \"{profile_key}\"' msg.format(dotfile_key, profile_key) self._dbg(msg) self._dirty = True @@ -383,9 +383,9 @@ class CfgYaml: if old == chmod: return False if self._debug: - self._dbg('update dotfile: {}'.format(key)) - self._dbg('old chmod value: {}'.format(old)) - self._dbg('new chmod value: {}'.format(chmod)) + self._dbg(f'update dotfile: {key}') + self._dbg(f'old chmod value: {old}') + self._dbg(f'new chmod value: {chmod}') dotfile = self._yaml_dict[self.key_dotfiles][key] if not chmod: del dotfile[self.key_dotfile_chmod] @@ -400,13 +400,13 @@ class CfgYaml: if key in self.dotfiles.keys(): return False if self._debug: - self._dbg('adding new dotfile: {}'.format(key)) - self._dbg('new dotfile src: {}'.format(src)) - self._dbg('new dotfile dst: {}'.format(dst)) - self._dbg('new dotfile link: {}'.format(link)) - self._dbg('new dotfile chmod: {}'.format(chmod)) - self._dbg('new dotfile trans_r: {}'.format(trans_r_key)) - self._dbg('new dotfile trans_w: {}'.format(trans_w_key)) + self._dbg(f'adding new dotfile: {key}') + self._dbg(f'new dotfile src: {src}') + self._dbg(f'new dotfile dst: {dst}') + self._dbg(f'new dotfile link: {link}') + self._dbg(f'new dotfile chmod: {chmod}') + self._dbg(f'new dotfile trans_r: {trans_r_key}') + self._dbg(f'new dotfile trans_w: {trans_w_key}') # create the dotfile dict df_dict = { @@ -430,7 +430,7 @@ class CfgYaml: df_dict[self.key_trans_w] = str(trans_w_key) if self._debug: - self._dbg('dotfile dict: {}'.format(df_dict)) + self._dbg(f'dotfile dict: {df_dict}') # add to global dict self._yaml_dict[self.key_dotfiles][key] = df_dict @@ -440,26 +440,26 @@ class CfgYaml: def del_dotfile(self, key): """remove this dotfile from config""" if key not in self._yaml_dict[self.key_dotfiles]: - self._log.err('key not in dotfiles: {}'.format(key)) + self._log.err(f'key not in dotfiles: {key}') return False if self._debug: - self._dbg('remove dotfile: {}'.format(key)) + self._dbg(f'remove dotfile: {key}') del self._yaml_dict[self.key_dotfiles][key] if self._debug: dfs = self._yaml_dict[self.key_dotfiles] - self._dbg('new dotfiles: {}'.format(dfs)) + self._dbg(f'new dotfiles: {dfs}') self._dirty = True return True def del_dotfile_from_profile(self, df_key, pro_key): """remove this dotfile from that profile""" if self._debug: - self._dbg('removing \"{}\" from \"{}\"'.format(df_key, pro_key)) + self._dbg(f'removing \"{df_key}\" from \"{pro_key}\"') if df_key not in self.dotfiles.keys(): - self._log.err('key not in dotfiles: {}'.format(df_key)) + self._log.err(f'key not in dotfiles: {df_key}') return False if pro_key not in self.profiles.keys(): - self._log.err('key not in profile: {}'.format(pro_key)) + self._log.err(f'key not in profile: {pro_key}') return False # get the profile dictionary profile = self._yaml_dict[self.key_profiles][pro_key] @@ -470,12 +470,12 @@ class CfgYaml: return True if self._debug: dfs = profile[self.key_profile_dotfiles] - self._dbg('{} profile dotfiles: {}'.format(pro_key, dfs)) - self._dbg('remove {} from profile {}'.format(df_key, pro_key)) + self._dbg(f'{pro_key} profile dotfiles: {dfs}') + self._dbg(f'remove {df_key} from profile {pro_key}') profile[self.key_profile_dotfiles].remove(df_key) if self._debug: dfs = profile[self.key_profile_dotfiles] - self._dbg('{} profile dotfiles: {}'.format(pro_key, dfs)) + self._dbg(f'{pro_key} profile dotfiles: {dfs}') self._dirty = True return True @@ -493,13 +493,13 @@ class CfgYaml: # save to file if self._debug: - self._dbg('saving to {}'.format(self._path)) + self._dbg(f'saving to {self._path}') try: with open(self._path, 'w', encoding='utf8') as file: self._yaml_dump(content, file, fmt=self._config_format) except Exception as exc: self._log.err(exc) - err = 'error saving config: {}'.format(self._path) + err = f'error saving config: {self._path}' raise YamlException(err) from exc if self._dirty_deprecated: @@ -558,7 +558,7 @@ class CfgYaml: keys = dotfiles.keys() if len(keys) != len(list(set(keys))): dups = [x for x in keys if x not in list(set(keys))] - err = 'duplicate dotfile keys found: {}'.format(dups) + err = f'duplicate dotfile keys found: {dups}' raise YamlException(err) dotfiles = self._norm_dotfiles(dotfiles) @@ -645,7 +645,7 @@ class CfgYaml: if name in current: # ignore if already defined if self._debug: - self._dbg('ignore uservariables {}'.format(name)) + self._dbg(f'ignore uservariables {name}') continue content = userinput(prompt, debug=self._debug) uvars[name] = content @@ -764,24 +764,24 @@ class CfgYaml: if self.key_dotfile_chmod in val: value = str(val[self.key_dotfile_chmod]) if len(value) < 3: - err = 'bad format for chmod: {}'.format(value) + err = f'bad format for chmod: {value}' self._log.err(err) - raise YamlException('config content error: {}'.format(err)) + raise YamlException(f'config content error: {err}') try: int(value) except Exception as exc: - err = 'bad format for chmod: {}'.format(value) + err = f'bad format for chmod: {value}' self._log.err(err) - err = 'config content error: {}'.format(err) + err = f'config content error: {err}' raise YamlException(err) from exc # normalize chmod value for chmodv in list(value): chmodint = int(chmodv) if chmodint < 0 or chmodint > 7: - err = 'bad format for chmod: {}'.format(value) + err = f'bad format for chmod: {value}' self._log.err(err) raise YamlException( - 'config content error: {}'.format(err) + f'config content error: {err}' ) val[self.key_dotfile_chmod] = int(value, 8) @@ -877,7 +877,7 @@ class CfgYaml: continue if self.key_all in dfs: if self._debug: - self._dbg('add ALL to profile \"{}\"'.format(k)) + self._dbg(f'add ALL to profile \"{k}\"') val[self.key_profile_dotfiles] = self.dotfiles.keys() def _resolve_profile_includes(self): @@ -903,17 +903,15 @@ class CfgYaml: return dotfiles, actions if self._debug: - self._dbg('{} includes {}'.format(profile, ','.join(includes))) - self._dbg('{} dotfiles before include: {}'.format(profile, - dotfiles)) - self._dbg('{} actions before include: {}'.format(profile, - actions)) + incs = ','.join(includes) + self._dbg(f'{profile} includes {incs}') + self._dbg(f'{profile} dotfiles before include: {dotfiles}') + self._dbg(f'{profile} actions before include: {actions}') seen = [] for i in uniq_list(includes): if self._debug: - self._dbg('resolving includes "{}" <- "{}"' - .format(profile, i)) + self._dbg(f'resolving includes "{profile}" <- "{i}"') # ensure no include loop occurs if i in seen: @@ -921,37 +919,36 @@ class CfgYaml: seen.append(i) # included profile even exists if i not in self.profiles.keys(): - self._log.warn('include unknown profile: {}'.format(i)) + self._log.warn(f'include unknown profile: {i}') continue # recursive resolve if self._debug: - self._dbg('recursively resolving includes for profile "{}"' - .format(i)) + self._dbg(f'recursively resolving includes for profile "{i}"') o_dfs, o_actions = self._rec_resolve_profile_include(i) # merge dotfile keys if self._debug: - self._dbg('Merging dotfiles {} <- {}: {} <- {}' - .format(profile, i, dotfiles, o_dfs)) + msg = f'Merging dotfiles {profile}' + msg += f' <- {i}: {dotfiles} <- {o_dfs}' + self._dbg(msg) dotfiles.extend(o_dfs) this_profile[self.key_profile_dotfiles] = uniq_list(dotfiles) # merge actions keys if self._debug: - self._dbg('Merging actions {} <- {}: {} <- {}' - .format(profile, i, actions, o_actions)) - actions.extend(o_actions) + msg = f'Merging actions {profile} ' + msg += '<- {i}: {actions} <- {o_actions}' + self._dbg(msg) + actions.extend(o_actions) this_profile[self.key_profile_actions] = uniq_list(actions) dotfiles = this_profile.get(self.key_profile_dotfiles, []) actions = this_profile.get(self.key_profile_actions, []) if self._debug: - self._dbg('{} dotfiles after include: {}'.format(profile, - dotfiles)) - self._dbg('{} actions after include: {}'.format(profile, - actions)) + self._dbg(f'{profile} dotfiles after include: {dotfiles}') + self._dbg(f'{profile} actions after include: {actions}') # since included items are resolved here # we can clear these include @@ -971,11 +968,11 @@ class CfgYaml: newvars = {} for path in paths: if self._debug: - self._dbg('import variables from {}'.format(path)) + self._dbg(f'import variables from {path}') var = self._import_sub(path, self.key_variables, mandatory=False) if self._debug: - self._dbg('import dynvariables from {}'.format(path)) + self._dbg(f'import dynvariables from {path}') dvar = self._import_sub(path, self.key_dvariables, mandatory=False) @@ -997,7 +994,7 @@ class CfgYaml: paths = self._resolve_paths(paths) for path in paths: if self._debug: - self._dbg('import actions from {}'.format(path)) + self._dbg(f'import actions from {path}') new = self._import_sub(path, self.key_actions, mandatory=False, patch_func=self._norm_actions) @@ -1010,7 +1007,7 @@ class CfgYaml: if not imp: continue if self._debug: - self._dbg('import dotfiles for profile {}'.format(k)) + self._dbg(f'import dotfiles for profile {k}') paths = self._resolve_paths(imp) for path in paths: current = val.get(self.key_dotfiles, []) @@ -1021,9 +1018,9 @@ class CfgYaml: def _import_config(self, path): """import config from path""" if self._debug: - self._dbg('import config from {}'.format(path)) - self._dbg('profile: {}'.format(self._profile)) - self._dbg('included profiles: {}'.format(self._inc_profiles)) + self._dbg(f'import config from {path}') + self._dbg(f'profile: {self._profile}') + self._dbg(f'included profiles: {self._inc_profiles}') sub = CfgYaml(path, profile=self._profile, addprofiles=self._inc_profiles, debug=self._debug, @@ -1065,8 +1062,7 @@ class CfgYaml: paths = self._resolve_paths(imp) for path in paths: if path in self.imported_configs: - err = '{} imported more than once in {}'.format(path, - self._path) + err = f'{path} imported more than once in {self._path}' raise YamlException(err) self._import_config(path) @@ -1076,19 +1072,19 @@ class CfgYaml: patch_func is applied to each element if defined """ if self._debug: - self._dbg('import \"{}\" from \"{}\"'.format(key, path)) + self._dbg(f'import \"{key}\" from \"{path}\"') extdict = self._load_yaml(path) new = self._get_entry(extdict, key, mandatory=mandatory) if patch_func: if self._debug: - self._dbg('calling patch: {}'.format(patch_func)) + self._dbg(f'calling patch: {patch_func}') new = patch_func(new) if not new and mandatory: - err = 'no \"{}\" imported from \"{}\"'.format(key, path) + err = f'no \"{key}\" imported from \"{path}\"' self._log.warn(err) raise YamlException(err) if self._debug: - self._dbg('imported \"{}\": {}'.format(key, new)) + self._dbg(f'imported \"{key}\": {new}') return new ######################################################## @@ -1106,7 +1102,7 @@ class CfgYaml: self.key_profile_dotfiles: [] } if self._debug: - self._dbg('adding new profile: {}'.format(key)) + self._dbg(f'adding new profile: {key}') self._dirty = True ######################################################## @@ -1159,7 +1155,7 @@ class CfgYaml: self._dirty = True self._dirty_deprecated = True warn = 'deprecated \"link: \"' - warn += ', updated to \"link: {}\"'.format(new) + warn += f', updated to \"link: {new}\"' self._log.warn(warn) if self.key_dotfile_link in dotfile and \ @@ -1171,7 +1167,7 @@ class CfgYaml: self._dirty = True self._dirty_deprecated = True warn = 'deprecated \"link: link\"' - warn += ', updated to \"link: {}\"'.format(new) + warn += f', updated to \"link: {new}\"' self._log.warn(warn) if old_key in dotfile and \ @@ -1186,7 +1182,7 @@ class CfgYaml: self._dirty = True self._dirty_deprecated = True warn = 'deprecated \"link_children\" value' - warn += ', updated to \"{}\"'.format(new) + warn += f', updated to \"{new}\"' self._log.warn(warn) ######################################################## @@ -1209,22 +1205,22 @@ class CfgYaml: """load a yaml file to a dict""" content = {} if self._debug: - self._dbg('----------dump:{}----------'.format(path)) + self._dbg(f'----------dump:{path}----------') cfg = '\n' with open(path, 'r', encoding='utf8') as file: for line in file: cfg += line self._dbg(cfg.rstrip()) - self._dbg('----------end:{}----------'.format(path)) + self._dbg(f'----------end:{path}----------') try: content, fmt = self._yaml_load(path) self._config_format = fmt except Exception as exc: self._log.err(exc) - err = 'config yaml error: {}'.format(path) + err = f'config yaml error: {path}' raise YamlException(err) from exc if self._debug: - self._dbg('format: {}'.format(self._config_format)) + self._dbg(f'format: {self._config_format}') return content def _validate(self, yamldict): @@ -1235,9 +1231,9 @@ class CfgYaml: # check top entries for entry in self.top_entries: if entry not in yamldict: - err = 'no {} entry found'.format(entry) + err = f'no {entry} entry found'.format(entry) self._log.err(err) - raise YamlException('config format error: {}'.format(err)) + raise YamlException(f'config format error: {err}') # check link_dotfile_default if self.key_settings not in yamldict: @@ -1253,10 +1249,10 @@ class CfgYaml: return val = settings[self.key_settings_link_dotfile_default] if val not in self.allowed_link_val: - err = 'bad link value: {}'.format(val) + err = f'bad link value: {val}' self._log.err(err) - self._log.err('allowed: {}'.format(self.allowed_link_val)) - raise YamlException('config content error: {}'.format(err)) + self._log.err(f'allowed: {self.allowed_link_val}') + raise YamlException(f'config content error: {err}') @classmethod def _yaml_load(cls, path): @@ -1352,7 +1348,7 @@ class CfgYaml: for entry in entries: newe = self._template_item(entry) if self._debug and entry != newe: - self._dbg('resolved: {} -> {}'.format(entry, newe)) + self._dbg(f'resolved: {entry} -> {newe}') new.append(newe) return new @@ -1364,7 +1360,7 @@ class CfgYaml: for k, val in entries.items(): newv = self._template_item(val) if self._debug and val != newv: - self._dbg('resolved: {} -> {}'.format(val, newv)) + self._dbg(f'resolved: {val} -> {newv}') new[k] = newv return new @@ -1486,7 +1482,7 @@ class CfgYaml: def _parse_extended_import_path(self, path_entry): """Parse an import path in a tuple (path, fatal_not_found).""" if self._debug: - self._dbg('parsing path entry {}'.format(path_entry)) + self._dbg(f'parsing path entry {path_entry}') path, _, attribute = path_entry.rpartition(self.key_import_sep) fatal_not_found = attribute != self.key_import_ignore_key @@ -1500,18 +1496,20 @@ class CfgYaml: # str.rpartition # In both cases, path_entry is the path we're looking for. if self._debug: - self._dbg('using attribute default values for path {}' - .format(path_entry)) + msg = 'using attribute default values' + msg += f' for path {path_entry}' + self._dbg(msg) path = path_entry fatal_not_found = self.key_import_fatal_not_found elif self._debug: - self._dbg('path entry {} has fatal_not_found flag set to {}' - .format(path_entry, fatal_not_found)) + msg = f'path entry {path_entry} has fatal_not_found' + msg += f' flag set to {fatal_not_found}' + self._dbg(msg) return path, fatal_not_found def _handle_non_existing_path(self, path, fatal_not_found=True): """Raise an exception or log a warning to handle non-existing paths.""" - error = 'bad path {}'.format(path) + error = f'bad path {path}' if fatal_not_found: raise YamlException(error) self._log.warn(error) @@ -1520,7 +1518,7 @@ class CfgYaml: """Check if a path exists, raising if necessary.""" if os.path.exists(path): if self._debug: - self._dbg('path {} exists'.format(path)) + self._dbg(f'path {path} exists') return path self._handle_non_existing_path(path, fatal_not_found) @@ -1542,7 +1540,7 @@ class CfgYaml: paths = self._glob_path(path) if self._is_glob(path) else [path] if not paths: if self._debug: - self._dbg("glob path {} didn't expand".format(path)) + self._dbg(f"glob path {path} didn't expand") self._handle_non_existing_path(path, fatal_not_found) return [] @@ -1598,7 +1596,7 @@ class CfgYaml: """return copy of entry from yaml dictionary""" if key not in dic: if mandatory: - err = 'invalid config: no entry \"{}\" found'.format(key) + err = f'invalid config: no entry \"{key}\" found' raise YamlException(err) dic[key] = {} return deepcopy(dic[key]) @@ -1643,7 +1641,7 @@ class CfgYaml: def _glob_path(self, path): """Expand a glob.""" if self._debug: - self._dbg('expanding glob {}'.format(path)) + self._dbg(f'expanding glob {path}') expanded_path = os.path.expanduser(path) return glob.glob(expanded_path, recursive=True) @@ -1661,7 +1659,7 @@ class CfgYaml: path = ret ret = os.path.normpath(path) if self._debug and path != ret: - self._dbg('normalizing: {} -> {}'.format(path, ret)) + self._dbg(f'normalizing: {path} -> {ret}') return ret def _shell_exec_dvars(self, dic, keys=None): @@ -1672,11 +1670,11 @@ class CfgYaml: val = dic[k] ret, out = shellrun(val, debug=self._debug) if not ret: - err = 'var \"{}: {}\" failed: {}'.format(k, val, out) + err = f'var \"{k}: {val}\" failed: {out}' self._log.err(err) raise YamlException(err) if self._debug: - self._dbg('{}: `{}` -> {}'.format(k, val, out)) + self._dbg(f'{k}: `{val}` -> {out}') dic[k] = out @classmethod @@ -1687,7 +1685,7 @@ class CfgYaml: cur = ([int(x) for x in VERSION.split('.')]) cfg = ([int(x) for x in minversion.split('.')]) except Exception as exc: - err = 'bad version: \"{}\" VS \"{}\"'.format(VERSION, minversion) + err = f'bad version: \"{VERSION}\" VS \"{minversion}\"' raise YamlException(err) from exc if cur < cfg: err = 'current dotdrop version is too old for that config file.' @@ -1711,21 +1709,21 @@ class CfgYaml: """pretty print dict""" if not self._debug: return - self._dbg('{}:'.format(title)) + self._dbg(f'{title}:') if not elems: return for k, val in elems.items(): if isinstance(val, dict): - self._dbg(' - \"{}\"'.format(k)) + self._dbg(f' - \"{k}\"') for subkey, sub in val.items(): - self._dbg(' * {}: \"{}\"'.format(subkey, sub)) + self._dbg(f' * {subkey}: \"{sub}\"') else: - self._dbg(' - \"{}\": {}'.format(k, val)) + self._dbg(f' - \"{k}\": {val}') def _dbg(self, content): directory = os.path.basename(os.path.dirname(self._path)) pre = os.path.join(directory, os.path.basename(self._path)) - self._log.dbg('[{}] {}'.format(pre, content)) + self._log.dbg(f'[{pre}] {content}') def _save_uservariables(self, uvars): """save uservariables to file""" @@ -1737,7 +1735,7 @@ class CfgYaml: if cnt == 0: name = self.save_uservariables_name.format('') else: - name = self.save_uservariables_name.format('-{}'.format(cnt)) + name = self.save_uservariables_name.format(f'-{cnt}') cnt += 1 path = os.path.join(parent, name) @@ -1748,12 +1746,12 @@ class CfgYaml: content = {'variables': uvars} try: if self._debug: - self._dbg('saving uservariables values to {}'.format(path)) + self._dbg(f'saving uservariables values to {path}') with open(path, 'w', encoding='utf8') as file: self._yaml_dump(content, file, fmt=self._config_format) except Exception as exc: # self._log.err(exc) - err = 'error saving uservariables to {}'.format(path) + err = f'error saving uservariables to {path}' self._log.err(err) raise YamlException(err) from exc - self._log.log('uservariables values saved to {}'.format(path)) + self._log.log(f'uservariables values saved to {path}') diff --git a/dotdrop/comparator.py b/dotdrop/comparator.py index 119ee79..7b0514a 100644 --- a/dotdrop/comparator.py +++ b/dotdrop/comparator.py @@ -38,33 +38,28 @@ class Comparator: ignore = [] local_path = os.path.expanduser(local_path) deployed_path = os.path.expanduser(deployed_path) - self.log.dbg('comparing {} and {}'.format( - local_path, - deployed_path, - )) - self.log.dbg('ignore pattern(s): {}'.format(ignore)) + self.log.dbg(f'comparing {local_path} and {deployed_path}') + self.log.dbg(f'ignore pattern(s): {ignore}') # test type of file if os.path.isdir(local_path) and not os.path.isdir(deployed_path): - return '\"{}\" is a dir while \"{}\" is a file\n'.format( - local_path, - deployed_path, - ) + ret = f'\"{local_path}\" is a dir' + ret += f' while \"{deployed_path}\" is a file\n' + return ret if not os.path.isdir(local_path) and os.path.isdir(deployed_path): - return '\"{}\" is a file while \"{}\" is a dir\n'.format( - local_path, - deployed_path, - ) + ret = f'\"{local_path}\" is a file' + ret += f' while \"{deployed_path}\" is a dir\n' + return ret # test content if not os.path.isdir(local_path): - self.log.dbg('{} is a file'.format(local_path)) + self.log.dbg(f'{local_path} is a file') ret = self._comp_file(local_path, deployed_path, ignore) if not ret: ret = self._comp_mode(local_path, deployed_path, mode=mode) return ret - self.log.dbg('{} is a directory'.format(local_path)) + self.log.dbg(f'{local_path} is a directory') ret = self._comp_dir(local_path, deployed_path, ignore) if not ret: @@ -82,79 +77,68 @@ class Comparator: deployed_mode = get_file_perm(deployed_path) if local_mode == deployed_mode: return '' - msg = 'mode differ {} ({:o}) and {} ({:o})' - self.log.dbg(msg.format(local_path, local_mode, deployed_path, - deployed_mode)) - ret = 'modes differ for {} ({:o}) vs {:o}\n' - return ret.format(deployed_path, deployed_mode, local_mode) + msg = f'mode differ {local_path} ({local_mode:o}) ' + msg += f'and {deployed_path} ({deployed_mode:o})' + self.log.dbg(msg) + ret = f'modes differ for {deployed_path} ' + ret += f'({deployed_mode:o}) vs {local_mode:o}\n' + return ret def _comp_file(self, local_path, deployed_path, ignore): """compare a file""" - self.log.dbg('compare file {} with {}'.format( - local_path, - deployed_path, - )) + self.log.dbg(f'compare file {local_path} with {deployed_path}') if (self.ignore_missing_in_dotdrop and not os.path.exists(local_path)) \ or must_ignore([local_path, deployed_path], ignore, debug=self.debug): - self.log.dbg('ignoring diff {} and {}'.format( - local_path, - deployed_path, - )) + self.log.dbg(f'ignoring diff {local_path} and {deployed_path}') return '' return self._diff(local_path, deployed_path) def _comp_dir(self, local_path, deployed_path, ignore): """compare a directory""" - self.log.dbg('compare directory {} with {}'.format( - local_path, - deployed_path, - )) + self.log.dbg(f'compare directory {local_path} with {deployed_path}') if not os.path.exists(deployed_path): return '' if (self.ignore_missing_in_dotdrop and not os.path.exists(local_path)) \ or must_ignore([local_path, deployed_path], ignore, debug=self.debug): - self.log.dbg('ignoring diff {} and {}'.format( - local_path, - deployed_path, - )) + self.log.dbg(f'ignoring diff {local_path} and {deployed_path}') return '' if not os.path.isdir(deployed_path): - return '\"{}\" is a file\n'.format(deployed_path) + return f'\"{deployed_path}\" is a file\n' return self._compare_dirs(local_path, deployed_path, ignore) def _compare_dirs(self, local_path, deployed_path, ignore): """compare directories""" - self.log.dbg('compare {} and {}'.format(local_path, deployed_path)) + self.log.dbg(f'compare {local_path} and {deployed_path}') ret = [] comp = filecmp.dircmp(local_path, deployed_path) # handle files only in deployed dir - self.log.dbg('files only in deployed dir: {}'.format(comp.left_only)) + self.log.dbg(f'files only in deployed dir: {comp.left_only}') for i in comp.left_only: if self.ignore_missing_in_dotdrop or \ must_ignore([os.path.join(local_path, i)], ignore, debug=self.debug): continue - ret.append('=> \"{}\" does not exist on destination\n'.format(i)) + ret.append(f'=> \"{i}\" does not exist on destination\n') # handle files only in dotpath dir - self.log.dbg('files only in dotpath dir: {}'.format(comp.right_only)) + self.log.dbg(f'files only in dotpath dir: {comp.right_only}') for i in comp.right_only: if must_ignore([os.path.join(deployed_path, i)], ignore, debug=self.debug): continue if not self.ignore_missing_in_dotdrop: - ret.append('=> \"{}\" does not exist in dotdrop\n'.format(i)) + ret.append(f'=> \"{i}\" does not exist in dotdrop\n') # same local_path and deployed_path but different type funny = comp.common_funny - self.log.dbg('files with different types: {}'.format(funny)) + self.log.dbg(f'files with different types: {funny}') for i in funny: source_file = os.path.join(local_path, i) deployed_file = os.path.join(deployed_path, i) @@ -166,13 +150,13 @@ class Comparator: continue short = os.path.basename(source_file) # file vs dir - ret.append('=> different type: \"{}\"\n'.format(short)) + ret.append(f'=> different type: \"{short}\"\n') # content is different funny = comp.diff_files funny.extend(comp.funny_files) funny = uniq_list(funny) - self.log.dbg('files with different content: {}'.format(funny)) + self.log.dbg(f'files with different content: {funny}') for i in funny: source_file = os.path.join(local_path, i) deployed_file = os.path.join(deployed_path, i) @@ -198,5 +182,5 @@ class Comparator: diff_cmd=self.diff_cmd, debug=self.debug) if header: lshort = os.path.basename(local_path) - out = '=> diff \"{}\":\n{}'.format(lshort, out) + out = f'=> diff \"{lshort}\":\n{out}' return out diff --git a/dotdrop/dotfile.py b/dotdrop/dotfile.py index 65712e9..88fdff2 100644 --- a/dotdrop/dotfile.py +++ b/dotdrop/dotfile.py @@ -57,11 +57,10 @@ class Dotfile(DictParser): if self.link != LinkTypes.NOLINK and \ ( - (trans_r and len(trans_r) > 0) - or + (trans_r and len(trans_r) > 0) or (trans_w and len(trans_w) > 0) ): - msg = '[{}] transformations disabled'.format(key) + msg = f'[{key}] transformations disabled' msg += ' because dotfile is linked' self.log.warn(msg) self.trans_r = [] @@ -112,48 +111,48 @@ class Dotfile(DictParser): return hash(self.dst) ^ hash(self.src) ^ hash(self.key) def __str__(self): - msg = 'key:\"{}\"'.format(self.key) - msg += ', src:\"{}\"'.format(self.src) - msg += ', dst:\"{}\"'.format(self.dst) - msg += ', link:\"{}\"'.format(str(self.link)) - msg += ', template:{}'.format(self.template) + msg = f'key:\"{self.key}\"' + msg += f', src:\"{self.src}\"' + msg += f', dst:\"{self.dst}\"' + msg += f', link:\"{self.link}\"' + msg += f', template:{self.template}' if self.chmod: - msg += ', chmod:{:o}'.format(self.chmod) + msg += f', chmod:{self.chmod:o}' return msg 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{}template: \"{}\"'.format(indent, str(self.template)) + out = f'dotfile: \"{self.key}\"' + out += f'\n{indent}src: \"{self.src}\"' + out += f'\n{indent}dst: \"{self.dst}\"' + out += f'\n{indent}link: \"{self.link}\"' + out += f'\n{indent}template: \"{self.template}\"' if self.chmod: - out += '\n{}chmod: \"{:o}\"'.format(indent, self.chmod) + out += f'\n{indent}chmod: \"{self.chmod:o}\"' - out += '\n{}pre-action:'.format(indent) + out += f'\n{indent}pre-action:' some = self.get_pre_actions() if some: for act in some: - out += '\n{}- {}'.format(2 * indent, act) + out += f'\n{2*indent}- {act}' - out += '\n{}post-action:'.format(indent) + out += f'\n{indent}post-action:' some = self.get_post_actions() if some: for act in some: - out += '\n{}- {}'.format(2 * indent, act) + out += f'\n{2*indent}- {act}' - out += '\n{}trans_r:'.format(indent) + out += f'\n{indent}trans_r:' some = self.get_trans_r() if some: - out += '\n{}- {}'.format(2 * indent, some) + out += f'\n{2*indent}- {some}' - out += '\n{}trans_w:'.format(indent) + out += f'\n{indent}trans_w:' some = self.get_trans_w() if some: - out += '\n{}- {}'.format(2 * indent, some) + out += f'\n{2*indent}- {some}' return out def __repr__(self): - return 'dotfile({!s})'.format(self) + return f'dotfile({self})' diff --git a/dotdrop/importer.py b/dotdrop/importer.py index ecbe2df..d9f1760 100644 --- a/dotdrop/importer.py +++ b/dotdrop/importer.py @@ -67,9 +67,9 @@ class Importer: -1: error """ path = os.path.abspath(path) - self.log.dbg('import {}'.format(path)) + self.log.dbg(f'import {path}'.format(path)) if not os.path.exists(path): - self.log.err('\"{}\" does not exist, ignored!'.format(path)) + self.log.err(f'\"{path}\" does not exist, ignored!') return -1 # check transw if any @@ -110,8 +110,8 @@ class Importer: if self.safe: realdst = os.path.realpath(dst) if dst != realdst: - msg = '\"{}\" is a symlink, dereference it and continue?' - if not self.log.ask(msg.format(dst)): + msg = f'\"{dst}\" is a symlink, dereference it and continue?' + if not self.log.ask(msg): return 0 # create src path @@ -122,7 +122,7 @@ class Importer: src = src.rstrip(os.sep) src = os.path.abspath(src) src = strip_home(src) - self.log.dbg('import src for {} as {}'.format(dst, src)) + self.log.dbg(f'import src for {dst} as {src}') # with or without dot prefix strip = '.' + os.sep if self.keepdot: @@ -136,13 +136,13 @@ class Importer: linktype = import_link if linktype == LinkTypes.LINK_CHILDREN and \ not os.path.isdir(path): - self.log.err('importing \"{}\" failed!'.format(path)) + self.log.err(f'importing \"{path}\" failed!') return -1 if self._already_exists(src, dst): return -1 - self.log.dbg('import dotfile: src:{} dst:{}'.format(src, dst)) + self.log.dbg(f'import dotfile: src:{src} dst:{dst}') if not self._import_file(src, dst, trans_write=trans_write): return -1 @@ -165,7 +165,7 @@ class Importer: # handle file mode chmod = None dflperm = get_default_file_perms(dst, self.umask) - self.log.dbg('import chmod: {}'.format(import_mode)) + self.log.dbg(f'import chmod: {import_mode}') if import_mode or perm != dflperm: msg = 'adopt mode {:o} (umask {:o})' self.log.dbg(msg.format(perm, dflperm)) @@ -176,10 +176,10 @@ class Importer: trans_read=trans_r, trans_write=trans_w) if not retconf: - self.log.warn('\"{}\" ignored during import'.format(path)) + self.log.warn(f'\"{path}\" ignored during import') return 0 - self.log.sub('\"{}\" imported'.format(path)) + self.log.sub(f'\"{path}\" imported') return 1 def _check_existing_dotfile(self, src, dst): @@ -196,7 +196,7 @@ class Importer: diff = cmp.compare(src, dst) if diff != '': # files are different, dunno what to do - self.log.log('diff \"{}\" VS \"{}\"'.format(dst, src)) + self.log.log(f'diff \"{dst}\" VS \"{src}\"') self.log.emph(diff) # ask user msg = 'Dotfile \"{}\" already exists, overwrite?' @@ -225,18 +225,18 @@ class Importer: # create directory hierarchy if self.dry: - cmd = 'mkdir -p {}'.format(srcfd) - self.log.dry('would run: {}'.format(cmd)) + cmd = f'mkdir -p {srcfd}' + self.log.dry(f'would run: {cmd}') else: try: os.makedirs(srcfd, exist_ok=True) except OSError: - self.log.err('importing \"{}\" failed!'.format(dst)) + self.log.err(f'importing \"{dst}\" failed!') return False # import the file if self.dry: - self.log.dry('would copy {} to {}'.format(dst, srcf)) + self.log.dry(f'would copy {dst} to {srcf}') else: # apply trans_w dst = self._apply_trans_w(dst, trans_write) @@ -257,7 +257,7 @@ class Importer: except shutil.Error as exc: src = exc.args[0][0][0] why = exc.args[0][0][2] - self.log.err('importing \"{}\" failed: {}'.format(src, why)) + self.log.err(f'importing \"{src}\" failed: {why}') return True @@ -290,8 +290,8 @@ class Importer: def _ignore(self, path): if must_ignore([path], self.ignore, debug=self.debug): - self.log.dbg('ignoring import of {}'.format(path)) - self.log.warn('{} ignored'.format(path)) + self.log.dbg(f'ignoring import of {path}') + self.log.warn(f'{path} ignored') return True return False @@ -305,12 +305,12 @@ class Importer: """ if not trans: return path - self.log.dbg('executing write transformation {}'.format(trans)) + self.log.dbg(f'executing write transformation {trans}') tmp = get_unique_tmp_name() if not trans.transform(path, tmp, debug=self.debug, templater=self.templater): - msg = 'transformation \"{}\" failed for {}' - self.log.err(msg.format(trans.key, path)) + msg = f'transformation \"{trans.key}\" failed for {path}' + self.log.err(msg) if os.path.exists(tmp): removepath(tmp, logger=self.log) return None diff --git a/dotdrop/linktypes.py b/dotdrop/linktypes.py index 9e15c0e..08b2b9d 100644 --- a/dotdrop/linktypes.py +++ b/dotdrop/linktypes.py @@ -27,7 +27,7 @@ class LinkTypes(IntEnum): except KeyError as exc: if default: return default - err = 'bad {} value: "{}"'.format(cls.__name__, key) + err = f'bad {cls.__name__} value: "{key}"' raise ValueError(err) from exc def __str__(self): diff --git a/dotdrop/logger.py b/dotdrop/logger.py index 710af2d..1a78b2b 100644 --- a/dotdrop/logger.py +++ b/dotdrop/logger.py @@ -31,24 +31,23 @@ class Logger: cend = self._color(self.RESET) if bold: bold = self._color(self.BOLD) - fmt = '{}{}{}{}{}'.format(pre, cstart, bold, - string, cend) - fmt += '{}{}'.format(end, cend) + fmt = f'{pre}{cstart}{bold}{string}{cend}' + fmt += f'{end}{cend}' else: - fmt = '{}{}{}{}{}'.format(pre, cstart, string, end, cend) + fmt = f'{pre}{cstart}{string}{end}{cend}' sys.stdout.write(fmt) def sub(self, string, end='\n'): """sub log""" cstart = self._color(self.BLUE) cend = self._color(self.RESET) - sys.stdout.write('\t{}->{} {}{}'.format(cstart, cend, string, end)) + sys.stdout.write(f'\t{cstart}->{cend} {string}{end}') def emph(self, string, stdout=True): """emphasis log""" cstart = self._color(self.EMPH) cend = self._color(self.RESET) - content = '{}{}{}'.format(cstart, string, cend) + content = f'{cstart}{string}{cend}' if not stdout: sys.stderr.write(content) else: @@ -58,14 +57,14 @@ class Logger: """error log""" cstart = self._color(self.RED) cend = self._color(self.RESET) - msg = '{} {}'.format(string, end) - sys.stderr.write('{}[ERR] {}{}'.format(cstart, msg, cend)) + msg = f'{string} {end}'.format(string, end) + sys.stderr.write(f'{cstart}[ERR] {msg}{cend}') def warn(self, string, end='\n'): """warning log""" cstart = self._color(self.YELLOW) cend = self._color(self.RESET) - sys.stderr.write('{}[WARN] {} {}{}'.format(cstart, string, end, cend)) + sys.stderr.write(f'{cstart}[WARN] {string} {end}{cend}') def dbg(self, string, force=False): """debug log""" @@ -78,29 +77,28 @@ class Logger: cend = self._color(self.RESET) clight = self._color(self.LMAGENTA) bold = self._color(self.BOLD) - line = '{}{}[DEBUG][{}.{}]{}{} {}{}\n' - sys.stderr.write(line.format(bold, clight, - mod, func, - cend, cstart, - string, cend)) + line = f'{bold}{clight}[DEBUG][{mod}.{func}]' + line += f'{cend}{cstart} {string}{cend}\n' + sys.stderr.write(line) def dry(self, string, end='\n'): """dry run log""" cstart = self._color(self.GREEN) cend = self._color(self.RESET) - sys.stdout.write('{}[DRY] {} {}{}'.format(cstart, string, end, cend)) + sys.stdout.write(f'{cstart}[DRY] {string} {end}{cend}') @classmethod def raw(cls, string, end='\n'): """raw log""" - sys.stdout.write('{}{}'.format(string, end)) + sys.stdout.write(f'{string}{end}') def ask(self, query): """ask user for confirmation""" cstart = self._color(self.BLUE) cend = self._color(self.RESET) - query = '{}{}{}'.format(cstart, query + ' [y/N] ? ', cend) - resp = input(query) + question = query + ' [y/N] ? ' + qmsg = f'{cstart}{question}{cend}' + resp = input(qmsg) return resp == 'y' @classmethod diff --git a/dotdrop/options.py b/dotdrop/options.py index 8f6984a..d0b2f2a 100644 --- a/dotdrop/options.py +++ b/dotdrop/options.py @@ -38,9 +38,9 @@ if ENV_PROFILE in os.environ: NAME = 'dotdrop' CONFIGFILEYAML = 'config.yaml' CONFIGFILETOML = 'config.toml' -HOMECFG = '~/.config/{}'.format(NAME) -ETCXDGCFG = '/etc/xdg/{}'.format(NAME) -ETCCFG = '/etc/{}'.format(NAME) +HOMECFG = f'~/.config/{NAME}' +ETCXDGCFG = f'/etc/xdg/{NAME}' +ETCCFG = f'/etc/{NAME}' OPT_LINK = { LinkTypes.NOLINK.name.lower(): LinkTypes.NOLINK, @@ -51,11 +51,11 @@ OPT_LINK = { BANNER = r""" _ _ _ __| | ___ | |_ __| |_ __ ___ _ __ / _` |/ _ \| __/ _` | '__/ _ \| '_ | - \__,_|\___/ \__\__,_|_| \___/| .__/ v{} - |_|""".format(VERSION) + \__,_|\___/ \__\__,_|_| \___/| .__/ v{VERSION} + |_|""" -USAGE = """ -{} +USAGE = f""" +{BANNER} Usage: dotdrop install [-VbtfndDaW] [-c ] [-p ] @@ -89,7 +89,7 @@ Options: -L --file-only Do not show diff but only the files that differ. -m --preserve-mode Insert a chmod entry in the dotfile with its mode. -n --nodiff Do not diff when installing. - -p --profile= Specify the profile to use [default: {}]. + -p --profile= Specify the profile to use [default: {PROFILE}]. -P --show-patch Provide a one-liner to manually patch template. -s --as= Import as a different path from actual path. --transr= Associate trans_read key on import. @@ -102,7 +102,7 @@ Options: -z --ignore-missing Ignore files in installed folders that are missing. -v --version Show version. -h --help Show this screen. -""".format(BANNER, PROFILE) +""" class AttrMonitor: @@ -159,14 +159,15 @@ class Options(AttrMonitor): if not self.confpath: raise YamlException('no config file found') if not os.path.exists(self.confpath): - err = 'bad config file path: {}'.format(self.confpath) + err = f'bad config file path: {self.confpath}' raise YamlException(err) self.log.dbg('#################################################') self.log.dbg('#################### DOTDROP ####################') self.log.dbg('#################################################') - self.log.dbg('version: {}'.format(VERSION)) - self.log.dbg('command: {}'.format(' '.join(sys.argv))) - self.log.dbg('config file: {}'.format(self.confpath)) + self.log.dbg(f'version: {VERSION}') + args = ' '.join(sys.argv) + self.log.dbg(f'command: {args}') + self.log.dbg(f'config file: {self.confpath}') self._read_config() self._apply_args() @@ -290,7 +291,7 @@ class Options(AttrMonitor): self.compare_focus = self.args['--file'] self.compare_ignore = self.args['--ignore'] self.compare_ignore.extend(self.cmpignore) - self.compare_ignore.append('*{}'.format(self.install_backup_suffix)) + self.compare_ignore.append(f'*{self.install_backup_suffix}') self.compare_ignore = uniq_list(self.compare_ignore) self.compare_fileonly = self.args['--file-only'] self.ignore_missing_in_dotdrop = self.ignore_missing_in_dotdrop or \ @@ -303,7 +304,7 @@ class Options(AttrMonitor): self.import_mode = self.args['--preserve-mode'] or self.chmod_on_import self.import_ignore = self.args['--ignore'] self.import_ignore.extend(self.impignore) - self.import_ignore.append('*{}'.format(self.install_backup_suffix)) + self.import_ignore.append(f'*{self.install_backup_suffix}') self.import_ignore = uniq_list(self.import_ignore) self.import_transw = self.args['--transw'] self.import_transr = self.args['--transr'] @@ -314,7 +315,7 @@ class Options(AttrMonitor): self.update_iskey = self.args['--key'] self.update_ignore = self.args['--ignore'] self.update_ignore.extend(self.upignore) - self.update_ignore.append('*{}'.format(self.install_backup_suffix)) + self.update_ignore.append(f'*{self.install_backup_suffix}') self.update_ignore = uniq_list(self.update_ignore) self.update_showpatch = self.args['--show-patch'] @@ -362,7 +363,7 @@ class Options(AttrMonitor): # overwrite default import link with cli switch link = self.args['--link'] if link not in OPT_LINK: - self.log.err('bad option for --link: {}'.format(link)) + self.log.err(f'bad option for --link: {link}') sys.exit(USAGE) self.import_link = OPT_LINK[link] @@ -411,12 +412,12 @@ class Options(AttrMonitor): if callable(val): continue if isinstance(val, list): - debug_list('-> {}'.format(att), val, self.debug) + debug_list(f'-> {att}', val, self.debug) elif isinstance(val, dict): - debug_dict('-> {}'.format(att), val, self.debug) + debug_dict(f'-> {att}', val, self.debug) else: - self.log.dbg('-> {}: {}'.format(att, val)) + self.log.dbg(f'-> {att}: {val}') def _attr_set(self, attr): """error when some inexistent attr is set""" - raise Exception('bad option: {}'.format(attr)) + raise Exception(f'bad option: {attr}') diff --git a/dotdrop/profile.py b/dotdrop/profile.py index a117092..33a2928 100644 --- a/dotdrop/profile.py +++ b/dotdrop/profile.py @@ -55,8 +55,7 @@ class Profile(DictParser): hash(tuple(self.dotfiles))) def __str__(self): - msg = 'key:"{}"' - return msg.format(self.key) + return f'key:"{self.key}"' def __repr__(self): - return 'profile({!s})'.format(self) + return f'profile({self})' diff --git a/dotdrop/settings.py b/dotdrop/settings.py index da64b2b..30ce88f 100644 --- a/dotdrop/settings.py +++ b/dotdrop/settings.py @@ -115,7 +115,7 @@ class Settings(DictParser): # check diff command if not is_bin_in_path(self.diff_command): - err = 'bad diff_command: {}'.format(self.diff_command) + err = f'bad diff_command: {diff_command}' raise YamlException(err) def _serialize_seq(self, name, dic): diff --git a/dotdrop/templategen.py b/dotdrop/templategen.py index 03196de..b7f5f73 100644 --- a/dotdrop/templategen.py +++ b/dotdrop/templategen.py @@ -73,11 +73,11 @@ class Templategen: self._load_funcs_to_dic(jhelpers, self.env.globals) if func_file: for ffile in func_file: - self.log.dbg('load custom functions from {}'.format(ffile)) + self.log.dbg(f'load custom functions from {ffile}') self._load_path_to_dic(ffile, self.env.globals) if filter_file: for ffile in filter_file: - self.log.dbg('load custom filters from {}'.format(ffile)) + self.log.dbg(f'load custom filters from {ffile}') self._load_path_to_dic(ffile, self.env.filters) if self.debug: self._debug_dict('template additional variables', variables) @@ -93,7 +93,7 @@ class Templategen: try: return self._handle_file(src) except UndefinedError as exc: - err = 'undefined variable: {}'.format(exc.message) + err = f'undefined variable: {exc.message}' raise UndefinedException(err) from exc def generate_string(self, string): @@ -107,7 +107,7 @@ class Templategen: try: return self.env.from_string(string).render(self.variables) except UndefinedError as exc: - err = 'undefined variable: {}'.format(exc.message) + err = 'undefined variable: {exc.message}' raise UndefinedException(err) from exc def add_tmp_vars(self, newvars=None): @@ -129,7 +129,7 @@ class Templategen: def _load_path_to_dic(self, path, dic): mod = utils.get_module_from_path(path) if not mod: - self.log.warn('cannot load module \"{}\"'.format(path)) + self.log.warn(f'cannot load module \"{path}\"') return self._load_funcs_to_dic(mod, dic) @@ -139,13 +139,13 @@ class Templategen: return funcs = utils.get_module_functions(mod) for name, func in funcs: - self.log.dbg('load function \"{}\"'.format(name)) + self.log.dbg(f'load function \"{name}\"') dic[name] = func @classmethod def _header(cls, prepend=''): """add a comment usually in the header of a dotfile""" - return '{}{}'.format(prepend, utils.header()) + return f'{prepend}{utils.header()}' def _handle_file(self, src): """generate the file content from template""" @@ -161,8 +161,8 @@ class Templategen: self.log.dbg('using \"file\" for filetype identification') filetype = filetype.strip() istext = self._is_text(filetype) - self.log.dbg('filetype \"{}\": {}'.format(src, filetype)) - self.log.dbg('is text \"{}\": {}'.format(src, istext)) + self.log.dbg(f'filetype \"{src}\": {filetype}') + self.log.dbg(f'is text \"{src}\": {istext}') if not istext: return self._handle_bin_file(src) return self._handle_text_file(src) @@ -220,7 +220,7 @@ class Templategen: def is_template(path, ignore=None, debug=False): """recursively check if any file is a template within path""" if debug: - LOG.dbg('is template: {}'.format(path), force=True) + LOG.dbg(f'is template: {path}', force=True) path = os.path.expanduser(path) if not os.path.exists(path): @@ -280,8 +280,8 @@ class Templategen: """pretty print dict""" if not self.debug: return - self.log.dbg('{}:'.format(title)) + self.log.dbg(f'{title}:') if not elems: return for k, val in elems.items(): - self.log.dbg(' - \"{}\": {}'.format(k, val)) + self.log.dbg(f' - \"{k}\": {val}') diff --git a/dotdrop/utils.py b/dotdrop/utils.py index 6047ede..d4b109a 100644 --- a/dotdrop/utils.py +++ b/dotdrop/utils.py @@ -42,11 +42,13 @@ NOREMOVE = [os.path.normpath(p) for p in DONOTDELETE] def run(cmd, debug=False): """run a command (expects a list)""" if debug: - LOG.dbg('exec: {}'.format(' '.join(cmd)), force=True) - proc = subprocess.Popen(cmd, shell=False, - stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - out, _ = proc.communicate() - ret = proc.returncode + fcmd = ' '.join(cmd) + LOG.dbg(f'exec: {fcmd}', force=True) + with subprocess.Popen(cmd, shell=False, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) as proc: + out, _ = proc.communicate() + ret = proc.returncode out = out.splitlines(keepends=True) lines = ''.join([x.decode('utf-8', 'replace') for x in out]) return ret == 0, lines @@ -66,10 +68,10 @@ def shellrun(cmd, debug=False): returns True|False, output """ if debug: - LOG.dbg('shell exec: \"{}\"'.format(cmd), force=True) + LOG.dbg(f'shell exec: \"{cmd}\"', force=True) ret, out = subprocess.getstatusoutput(cmd) if debug: - LOG.dbg('shell result ({}): {}'.format(ret, out), force=True) + LOG.dbg(f'shell result ({ret}): {out}', force=True) return ret == 0, out @@ -79,11 +81,11 @@ def userinput(prompt, debug=False): return user input """ if debug: - LOG.dbg('get user input for \"{}\"'.format(prompt), force=True) - pre = 'Please provide the value for \"{}\": '.format(prompt) + LOG.dbg(f'get user input for \"{prompt}\"', force=True) + pre = f'Please provide the value for \"{prompt}\": ' res = input(pre) if debug: - LOG.dbg('user input result: {}'.format(res), force=True) + LOG.dbg(f'user input result: {res}', force=True) return res @@ -160,13 +162,13 @@ def removepath(path, logger=None): if not path: return if not os.path.lexists(path): - err = 'File not found: {}'.format(path) + err = f'File not found: {path}' if logger: logger.warn(err) return raise OSError(err) if os.path.normpath(os.path.expanduser(path)) in NOREMOVE: - err = 'Dotdrop refuses to remove {}'.format(path) + err = f'Dotdrop refuses to remove {path}' if logger: logger.warn(err) return @@ -178,7 +180,7 @@ def removepath(path, logger=None): elif os.path.isdir(path): shutil.rmtree(path) else: - err = 'Unsupported file type for deletion: {}'.format(path) + err = f'Unsupported file type for deletion: {path}' raise OSError(err) except Exception as exc: err = str(exc) @@ -226,7 +228,7 @@ def must_ignore(paths, ignores, debug=False): if not ignores: return False if debug: - LOG.dbg('must ignore? \"{}\" against {}'.format(paths, ignores), + LOG.dbg(f'must ignore? \"{paths}\" against {ignores}', force=True) ignored_negative, ignored = categorize( lambda ign: ign.startswith('!'), ignores) @@ -236,7 +238,7 @@ def must_ignore(paths, ignores, debug=False): for i in ignored: if fnmatch.fnmatch(path, i): if debug: - LOG.dbg('ignore \"{}\" match: {}'.format(i, path), + LOG.dbg(f'ignore \"{i}\" match: {path}', force=True) ignore_matches.append(path) @@ -249,24 +251,24 @@ def must_ignore(paths, ignores, debug=False): LOG.dbg(msg.format(path, nign), force=True) if fnmatch.fnmatch(path, nign): if debug: - msg = 'negative ignore \"{}\" match: {}'.format(nign, path) + msg = f'negative ignore \"{nign}\" match: {path}' LOG.dbg(msg, force=True) try: ignore_matches.remove(path) except ValueError: - LOG.warn('no files that are currently being ' - 'ignored match \"{}\". In order ' - 'for a negative ignore pattern ' - 'to work, it must match a file ' - 'that is being ignored by a ' - 'previous ignore pattern.'.format(nign) - ) + warn = 'no files that are currently being ' + warn += f'ignored match \"{nign}\". In order ' + warn += 'for a negative ignore pattern ' + warn += 'to work, it must match a file ' + warn += 'that is being ignored by a ' + warn += 'previous ignore pattern.' + LOG.warn(warn) if ignore_matches: if debug: - LOG.dbg('ignoring {}'.format(paths), force=True) + LOG.dbg(f'ignoring {paths}', force=True) return True if debug: - LOG.dbg('NOT ignoring {}'.format(paths), force=True) + LOG.dbg(f'NOT ignoring {paths}', force=True) return False @@ -284,7 +286,7 @@ def uniq_list(a_list): def patch_ignores(ignores, prefix, debug=False): """allow relative ignore pattern""" new = [] - LOG.dbg('ignores before patching: {}'.format(ignores), force=debug) + LOG.dbg(f'ignores before patching: {ignores}', force=debug) for ignore in ignores: negative = ignore.startswith('!') if negative: @@ -311,7 +313,7 @@ def patch_ignores(ignores, prefix, debug=False): new.append('!' + path) else: new.append(path) - LOG.dbg('ignores after patching: {}'.format(new), force=debug) + LOG.dbg(f'ignores after patching: {new}', force=debug) return new @@ -424,7 +426,7 @@ def get_file_perm(path): def chmod(path, mode, debug=False): """change mode of file""" if debug: - LOG.dbg('chmod {} {}'.format(oct(mode), path), force=True) + LOG.dbg(f'chmod {mode:o} {path}', force=True) os.chmod(path, mode) return get_file_perm(path) == mode @@ -452,23 +454,23 @@ def debug_list(title, elems, debug): """pretty print list""" if not debug: return - LOG.dbg('{}:'.format(title), force=debug) + LOG.dbg(f'{title}:', force=debug) for elem in elems: - LOG.dbg('\t- {}'.format(elem), force=debug) + LOG.dbg(f'\t- {elem}', force=debug) def debug_dict(title, elems, debug): """pretty print dict""" if not debug: return - LOG.dbg('{}:'.format(title), force=debug) + LOG.dbg(f'{title}:', force=debug) for k, val in elems.items(): if isinstance(val, list): - LOG.dbg('\t- \"{}\":'.format(k), force=debug) + LOG.dbg(f'\t- \"{k}\":', force=debug) for i in val: - LOG.dbg('\t\t- {}'.format(i), force=debug) + LOG.dbg(f'\t\t- {i}', force=debug) else: - LOG.dbg('\t- \"{}\": {}'.format(k, val), force=debug) + LOG.dbg(f'\t- \"{k}\": {val}', force=debug) def check_version(): @@ -493,14 +495,14 @@ def check_version(): def pivot_path(path, newdir, striphome=False, logger=None): """change path to be under newdir""" if logger: - logger.dbg('pivot new dir: \"{}\"'.format(newdir)) - logger.dbg('strip home: {}'.format(striphome)) + logger.dbg(f'pivot new dir: \"{newdir}\"') + logger.dbg(f'strip home: {striphome}') if striphome: path = strip_home(path) sub = path.lstrip(os.sep) new = os.path.join(newdir, sub) if logger: - logger.dbg('pivot \"{}\" to \"{}\"'.format(path, new)) + logger.dbg(f'pivot \"{path}\" to \"{new}\"') return new diff --git a/tests.sh b/tests.sh index de4738b..6788856 100755 --- a/tests.sh +++ b/tests.sh @@ -18,7 +18,9 @@ pyflakes --version which pycodestyle >/dev/null 2>&1 [ "$?" != "0" ] && echo "Install pycodestyle" && exit 1 echo "testing with pycodestyle" -pycodestyle --ignore=W503,W504,W605 dotdrop/ +# W503: Line break occurred before a binary operator +# W504: Line break occurred after a binary operator +pycodestyle --ignore=W503,W504 dotdrop/ pycodestyle tests/ pycodestyle scripts/ @@ -29,6 +31,15 @@ pyflakes tests/ # pylint echo "testing with pylint" +# https://pylint.pycqa.org/en/latest/user_guide/checkers/features.html +# R0902: too-many-instance-attributes +# R0913: too-many-arguments +# R0903: too-few-public-methods +# R0914: too-many-locals +# R0915: too-many-statements +# R0912: too-many-branches +# R0911: too-many-return-statements +# C0209: consider-using-f-string pylint \ --disable=R0902 \ --disable=R0913 \ @@ -37,7 +48,6 @@ pylint \ --disable=R0915 \ --disable=R0912 \ --disable=R0911 \ - --disable=R1732 \ --disable=C0209 \ dotdrop/