1
0
mirror of https://github.com/deadc0de6/dotdrop.git synced 2026-02-14 15:52:27 +00:00
This commit is contained in:
deadc0de6
2021-04-30 21:29:12 +02:00
parent cb71bf299f
commit 6a51a7abad
6 changed files with 188 additions and 163 deletions

View File

@@ -18,6 +18,8 @@ the upper layer:
Additionally a few methods are exported. Additionally a few methods are exported.
""" """
# pylint: disable=C0302
import os import os
import glob import glob
import io import io
@@ -31,7 +33,7 @@ from dotdrop.settings import Settings
from dotdrop.logger import Logger from dotdrop.logger import Logger
from dotdrop.templategen import Templategen from dotdrop.templategen import Templategen
from dotdrop.linktypes import LinkTypes from dotdrop.linktypes import LinkTypes
from dotdrop.utils import shell, uniq_list from dotdrop.utils import shellrun, uniq_list
from dotdrop.exceptions import YamlException, UndefinedException from dotdrop.exceptions import YamlException, UndefinedException
@@ -96,7 +98,7 @@ class CfgYaml:
allowed_link_val = [lnk_nolink, lnk_link, lnk_children] allowed_link_val = [lnk_nolink, lnk_link, lnk_children]
top_entries = [key_dotfiles, key_settings, key_profiles] top_entries = [key_dotfiles, key_settings, key_profiles]
def __init__(self, path, profile=None, addprofiles=[], debug=False): def __init__(self, path, profile=None, addprofiles=None, debug=False):
""" """
config parser config parser
@path: config file path @path: config file path
@@ -115,7 +117,7 @@ class CfgYaml:
# profile variables # profile variables
self._profilevarskeys = [] self._profilevarskeys = []
# included profiles # included profiles
self._inc_profiles = addprofiles self._inc_profiles = addprofiles or []
# init the dictionaries # init the dictionaries
self.settings = {} self.settings = {}
@@ -180,11 +182,11 @@ class CfgYaml:
# include the profile's variables/dynvariables last # include the profile's variables/dynvariables last
# as it overwrites existing ones # as it overwrites existing ones
self._inc_profiles, pv, pvd = self._get_profile_included_vars() self._inc_profiles, pvar, pdvar = self._get_profile_included_vars()
self._add_variables(pv, prio=True) self._add_variables(pvar, prio=True)
self._add_variables(pvd, shell=True, prio=True) self._add_variables(pdvar, shell=True, prio=True)
self._profilevarskeys.extend(pv.keys()) self._profilevarskeys.extend(pvar.keys())
self._profilevarskeys.extend(pvd.keys()) self._profilevarskeys.extend(pdvar.keys())
# template variables # template variables
self.variables = self._template_dict(self.variables) self.variables = self._template_dict(self.variables)
@@ -232,11 +234,11 @@ class CfgYaml:
self._resolve_profile_includes() self._resolve_profile_includes()
# add the current profile variables # add the current profile variables
_, pv, pvd = self._get_profile_included_vars() _, pvar, pdvar = self._get_profile_included_vars()
self._add_variables(pv, prio=False) self._add_variables(pvar, prio=False)
self._add_variables(pvd, shell=True, prio=False) self._add_variables(pdvar, shell=True, prio=False)
self._profilevarskeys.extend(pv.keys()) self._profilevarskeys.extend(pvar.keys())
self._profilevarskeys.extend(pvd.keys()) self._profilevarskeys.extend(pdvar.keys())
# resolve variables # resolve variables
self._clear_profile_vars(newvars) self._clear_profile_vars(newvars)
@@ -322,21 +324,21 @@ class CfgYaml:
"""update an existing dotfile""" """update an existing dotfile"""
if key not in self.dotfiles.keys(): if key not in self.dotfiles.keys():
return False return False
df = self._yaml_dict[self.key_dotfiles][key] dotfile = self._yaml_dict[self.key_dotfiles][key]
old = None old = None
if self.key_dotfile_chmod in df: if self.key_dotfile_chmod in dotfile:
old = df[self.key_dotfile_chmod] old = dotfile[self.key_dotfile_chmod]
if old == chmod: if old == chmod:
return False return False
if self._debug: if self._debug:
self._dbg('update dotfile: {}'.format(key)) self._dbg('update dotfile: {}'.format(key))
self._dbg('old chmod value: {}'.format(old)) self._dbg('old chmod value: {}'.format(old))
self._dbg('new chmod value: {}'.format(chmod)) self._dbg('new chmod value: {}'.format(chmod))
df = self._yaml_dict[self.key_dotfiles][key] dotfile = self._yaml_dict[self.key_dotfiles][key]
if not chmod: if not chmod:
del df[self.key_dotfile_chmod] del dotfile[self.key_dotfile_chmod]
else: else:
df[self.key_dotfile_chmod] = str(format(chmod, 'o')) dotfile[self.key_dotfile_chmod] = str(format(chmod, 'o'))
self._dirty = True self._dirty = True
return True return True
@@ -426,11 +428,12 @@ class CfgYaml:
if self._debug: if self._debug:
self._dbg('saving to {}'.format(self._path)) self._dbg('saving to {}'.format(self._path))
try: try:
with open(self._path, 'w') as f: with open(self._path, 'w') as file:
self._yaml_dump(content, f) self._yaml_dump(content, file)
except Exception as e: except Exception as exc:
self._log.err(e) self._log.err(exc)
raise YamlException('error saving config: {}'.format(self._path)) err = 'error saving config: {}'.format(self._path)
raise YamlException(err) from exc
if self._dirty_deprecated: if self._dirty_deprecated:
warn = 'your config contained deprecated entries' warn = 'your config contained deprecated entries'
@@ -587,11 +590,11 @@ class CfgYaml:
self.settings[self.key_import_variables] = new self.settings[self.key_import_variables] = new
# profile's import # profile's import
for k, v in self.profiles.items(): for _, val in self.profiles.items():
entries = v.get(self.key_import_profile_dfs, []) entries = val.get(self.key_import_profile_dfs, [])
new = self._template_list(entries) new = self._template_list(entries)
if new: if new:
v[self.key_import_profile_dfs] = new val[self.key_import_profile_dfs] = new
def _norm_actions(self, actions): def _norm_actions(self, actions):
""" """
@@ -601,12 +604,12 @@ class CfgYaml:
if not actions: if not actions:
return actions return actions
new = {} new = {}
for k, v in actions.items(): for k, val in actions.items():
if k == self.action_pre or k == self.action_post: if k in (self.action_pre, self.action_post):
for key, action in v.items(): for key, action in val.items():
new[key] = (k, action) new[key] = (k, action)
else: else:
new[k] = (self.action_post, v) new[k] = (self.action_post, val)
return new return new
def _norm_profiles(self, profiles): def _norm_profiles(self, profiles):
@@ -614,14 +617,14 @@ class CfgYaml:
if not profiles: if not profiles:
return profiles return profiles
new = {} new = {}
for k, v in profiles.items(): for k, val in profiles.items():
if not v: if not val:
# no dotfiles # no dotfiles
continue continue
# add dotfiles entry if not present # add dotfiles entry if not present
if self.key_profile_dotfiles not in v: if self.key_profile_dotfiles not in val:
v[self.key_profile_dotfiles] = [] val[self.key_profile_dotfiles] = []
new[k] = v new[k] = val
return new return new
def _norm_dotfiles(self, dotfiles): def _norm_dotfiles(self, dotfiles):
@@ -629,55 +632,56 @@ class CfgYaml:
if not dotfiles: if not dotfiles:
return dotfiles return dotfiles
new = {} new = {}
for k, v in dotfiles.items(): for k, val in dotfiles.items():
# add 'src' as key' if not present # add 'src' as key' if not present
if self.key_dotfile_src not in v: if self.key_dotfile_src not in val:
v[self.key_dotfile_src] = k val[self.key_dotfile_src] = k
new[k] = v new[k] = val
else: else:
new[k] = v new[k] = val
# fix deprecated trans key # fix deprecated trans key
if self.old_key_trans_r in v: if self.old_key_trans_r in val:
msg = '\"trans\" is deprecated, please use \"trans_read\"' msg = '\"trans\" is deprecated, please use \"trans_read\"'
self._log.warn(msg) self._log.warn(msg)
v[self.key_trans_r] = v[self.old_key_trans_r] val[self.key_trans_r] = val[self.old_key_trans_r]
del v[self.old_key_trans_r] del val[self.old_key_trans_r]
new[k] = v new[k] = val
if self.key_dotfile_link not in v: if self.key_dotfile_link not in val:
# apply link value if undefined # apply link value if undefined
val = self.settings[self.key_settings_link_dotfile_default] value = self.settings[self.key_settings_link_dotfile_default]
v[self.key_dotfile_link] = val val[self.key_dotfile_link] = value
# apply noempty if undefined # apply noempty if undefined
if self.key_dotfile_noempty not in v: if self.key_dotfile_noempty not in val:
val = self.settings.get(self.key_settings_noempty, False) value = self.settings.get(self.key_settings_noempty, False)
v[self.key_dotfile_noempty] = val val[self.key_dotfile_noempty] = value
# apply template if undefined # apply template if undefined
if self.key_dotfile_template not in v: if self.key_dotfile_template not in val:
val = self.settings.get(self.key_settings_template, True) value = self.settings.get(self.key_settings_template, True)
v[self.key_dotfile_template] = val val[self.key_dotfile_template] = value
# validate value of chmod if defined # validate value of chmod if defined
if self.key_dotfile_chmod in v: if self.key_dotfile_chmod in val:
val = str(v[self.key_dotfile_chmod]) value = str(val[self.key_dotfile_chmod])
if len(val) < 3: if len(value) < 3:
err = 'bad format for chmod: {}'.format(val) err = 'bad format for chmod: {}'.format(value)
self._log.err(err) self._log.err(err)
raise YamlException('config content error: {}'.format(err)) raise YamlException('config content error: {}'.format(err))
try: try:
int(val) int(value)
except Exception: except Exception as exc:
err = 'bad format for chmod: {}'.format(val) err = 'bad format for chmod: {}'.format(value)
self._log.err(err) self._log.err(err)
raise YamlException('config content error: {}'.format(err)) err = 'config content error: {}'.format(err)
raise YamlException(err) from exc
# normalize chmod value # normalize chmod value
for x in list(val): for chmodv in list(value):
y = int(x) chmodint = int(chmodv)
if y < 0 or y > 7: if chmodint < 0 or chmodint > 7:
err = 'bad format for chmod: {}'.format(val) err = 'bad format for chmod: {}'.format(value)
self._log.err(err) self._log.err(err)
raise YamlException( raise YamlException(
'config content error: {}'.format(err) 'config content error: {}'.format(err)
) )
v[self.key_dotfile_chmod] = int(val, 8) val[self.key_dotfile_chmod] = int(value, 8)
return new return new
@@ -719,13 +723,13 @@ class CfgYaml:
if profile: if profile:
variables['profile'] = profile variables['profile'] = profile
# add some more variables # add some more variables
p = self.settings.get(self.key_settings_dotpath) path = self.settings.get(self.key_settings_dotpath)
p = self._norm_path(p) path = self._norm_path(path)
variables['_dotdrop_dotpath'] = p variables['_dotdrop_dotpath'] = path
variables['_dotdrop_cfgpath'] = self._norm_path(self._path) variables['_dotdrop_cfgpath'] = self._norm_path(self._path)
p = self.settings.get(self.key_settings_workdir) path = self.settings.get(self.key_settings_workdir)
p = self._norm_path(p) path = self._norm_path(path)
variables['_dotdrop_workdir'] = p variables['_dotdrop_workdir'] = path
return variables return variables
def _get_profile_included_item(self, keyitem): def _get_profile_included_item(self, keyitem):
@@ -765,18 +769,18 @@ class CfgYaml:
def _resolve_profile_all(self): def _resolve_profile_all(self):
"""resolve some other parts of the config""" """resolve some other parts of the config"""
# profile -> ALL # profile -> ALL
for k, v in self.profiles.items(): for k, val in self.profiles.items():
dfs = v.get(self.key_profile_dotfiles, None) dfs = val.get(self.key_profile_dotfiles, None)
if not dfs: if not dfs:
continue continue
if self.key_all in dfs: if self.key_all in dfs:
if self._debug: if self._debug:
self._dbg('add ALL to profile \"{}\"'.format(k)) self._dbg('add ALL to profile \"{}\"'.format(k))
v[self.key_profile_dotfiles] = self.dotfiles.keys() val[self.key_profile_dotfiles] = self.dotfiles.keys()
def _resolve_profile_includes(self): def _resolve_profile_includes(self):
"""resolve profile(s) including other profiles""" """resolve profile(s) including other profiles"""
for k, v in self.profiles.items(): for k, _ in self.profiles.items():
self._rec_resolve_profile_include(k) self._rec_resolve_profile_include(k)
def _rec_resolve_profile_include(self, profile): def _rec_resolve_profile_include(self, profile):
@@ -860,7 +864,7 @@ class CfgYaml:
"""import external variables from paths""" """import external variables from paths"""
paths = self.settings.get(self.key_import_variables, None) paths = self.settings.get(self.key_import_variables, None)
if not paths: if not paths:
return return None
paths = self._resolve_paths(paths) paths = self._resolve_paths(paths)
newvars = {} newvars = {}
for path in paths: for path in paths:
@@ -899,18 +903,18 @@ class CfgYaml:
def _import_profiles_dotfiles(self): def _import_profiles_dotfiles(self):
"""import profile dotfiles""" """import profile dotfiles"""
for k, v in self.profiles.items(): for k, val in self.profiles.items():
imp = v.get(self.key_import_profile_dfs, None) imp = val.get(self.key_import_profile_dfs, None)
if not imp: if not imp:
continue continue
if self._debug: if self._debug:
self._dbg('import dotfiles for profile {}'.format(k)) self._dbg('import dotfiles for profile {}'.format(k))
paths = self._resolve_paths(imp) paths = self._resolve_paths(imp)
for path in paths: for path in paths:
current = v.get(self.key_dotfiles, []) current = val.get(self.key_dotfiles, [])
new = self._import_sub(path, self.key_dotfiles, new = self._import_sub(path, self.key_dotfiles,
mandatory=False) mandatory=False)
v[self.key_dotfiles] = new + current val[self.key_dotfiles] = new + current
def _import_config(self, path): def _import_config(self, path):
"""import config from path""" """import config from path"""
@@ -999,7 +1003,6 @@ class CfgYaml:
return return
self._fix_deprecated_link_by_default(yamldict) self._fix_deprecated_link_by_default(yamldict)
self._fix_deprecated_dotfile_link(yamldict) self._fix_deprecated_dotfile_link(yamldict)
return yamldict
def _fix_deprecated_link_by_default(self, yamldict): def _fix_deprecated_link_by_default(self, yamldict):
"""fix deprecated link_by_default""" """fix deprecated link_by_default"""
@@ -1028,9 +1031,9 @@ class CfgYaml:
return return
if not yamldict[self.key_dotfiles]: if not yamldict[self.key_dotfiles]:
return return
for k, dotfile in yamldict[self.key_dotfiles].items(): for _, dotfile in yamldict[self.key_dotfiles].items():
if self.key_dotfile_link in dotfile and \ if self.key_dotfile_link in dotfile and \
type(dotfile[self.key_dotfile_link]) is bool: isinstance(dotfile[self.key_dotfile_link], bool):
# patch link: <bool> # patch link: <bool>
cur = dotfile[self.key_dotfile_link] cur = dotfile[self.key_dotfile_link]
new = self.lnk_nolink new = self.lnk_nolink
@@ -1042,7 +1045,7 @@ class CfgYaml:
self._log.warn('deprecated \"link\" value') self._log.warn('deprecated \"link\" value')
elif old_key in dotfile and \ elif old_key in dotfile and \
type(dotfile[old_key]) is bool: isinstance(dotfile[old_key], bool):
# patch link_children: <bool> # patch link_children: <bool>
cur = dotfile[old_key] cur = dotfile[old_key]
new = self.lnk_nolink new = self.lnk_nolink
@@ -1076,16 +1079,17 @@ class CfgYaml:
if self._debug: if self._debug:
self._dbg('----------start:{}----------'.format(path)) self._dbg('----------start:{}----------'.format(path))
cfg = '\n' cfg = '\n'
with open(path, 'r') as f: with open(path, 'r') as file:
for line in f: for line in file:
cfg += line cfg += line
self._dbg(cfg.rstrip()) self._dbg(cfg.rstrip())
self._dbg('----------end:{}----------'.format(path)) self._dbg('----------end:{}----------'.format(path))
try: try:
content = self._yaml_load(path) content = self._yaml_load(path)
except Exception as e: except Exception as exc:
self._log.err(e) self._log.err(exc)
raise YamlException('config yaml error: {}'.format(path)) err = 'config yaml error: {}'.format(path)
raise YamlException(err) from exc
return content return content
@@ -1095,9 +1099,9 @@ class CfgYaml:
return return
# check top entries # check top entries
for e in self.top_entries: for entry in self.top_entries:
if e not in yamldict: if entry not in yamldict:
err = 'no {} entry found'.format(e) err = 'no {} entry found'.format(entry)
self._log.err(err) self._log.err(err)
raise YamlException('config format error: {}'.format(err)) raise YamlException('config format error: {}'.format(err))
@@ -1117,21 +1121,23 @@ class CfgYaml:
self._log.err(err) self._log.err(err)
raise YamlException('config content error: {}'.format(err)) raise YamlException('config content error: {}'.format(err))
def _yaml_load(self, path): @classmethod
def _yaml_load(cls, path):
"""load from yaml""" """load from yaml"""
with open(path, 'r') as f: with open(path, 'r') as file:
y = yaml() data = yaml()
y.typ = 'rt' data.typ = 'rt'
content = y.load(f) content = data.load(file)
return content return content
def _yaml_dump(self, content, where): @classmethod
def _yaml_dump(cls, content, where):
"""dump to yaml""" """dump to yaml"""
y = yaml() data = yaml()
y.default_flow_style = False data.default_flow_style = False
y.indent = 2 data.indent = 2
y.typ = 'rt' data.typ = 'rt'
y.dump(content, where) data.dump(content, where)
######################################################## ########################################################
# templating # templating
@@ -1156,9 +1162,9 @@ class CfgYaml:
val = item val = item
while Templategen.var_is_template(val): while Templategen.var_is_template(val):
val = self._tmpl.generate_string(val) val = self._tmpl.generate_string(val)
except UndefinedException as e: except UndefinedException as exc:
if exc_if_fail: if exc_if_fail:
raise e raise exc
return val return val
def _template_list(self, entries): def _template_list(self, entries):
@@ -1459,7 +1465,7 @@ class CfgYaml:
keys = dic.keys() keys = dic.keys()
for k in keys: for k in keys:
val = dic[k] val = dic[k]
ret, out = shell(val, debug=self._debug) ret, out = shellrun(val, debug=self._debug)
if not ret: if not ret:
err = 'var \"{}: {}\" failed: {}'.format(k, val, out) err = 'var \"{}: {}\" failed: {}'.format(k, val, out)
self._log.err(err) self._log.err(err)

View File

@@ -333,6 +333,7 @@ def cmd_install(opts):
# check result # check result
for fut in futures.as_completed(wait_for): for fut in futures.as_completed(wait_for):
tmpret, key, err = fut.result() tmpret, key, err = fut.result()
# check result
if tmpret: if tmpret:
installed.append(key) installed.append(key)
elif err: elif err:

View File

@@ -356,7 +356,9 @@ class Installer:
- False, 'aborted' : user aborted - False, 'aborted' : user aborted
""" """
overwrite = not self.safe overwrite = not self.safe
if os.path.lexists(dst): if os.path.lexists(dst):
# symlink exists
if os.path.realpath(dst) == os.path.realpath(src): if os.path.realpath(dst) == os.path.realpath(src):
msg = 'ignoring "{}", link already exists'.format(dst) msg = 'ignoring "{}", link already exists'.format(dst)
self.log.dbg(msg) self.log.dbg(msg)
@@ -369,22 +371,29 @@ class Installer:
msg = 'Remove "{}" for link creation?'.format(dst) msg = 'Remove "{}" for link creation?'.format(dst)
if self.safe and not self.log.ask(msg): if self.safe and not self.log.ask(msg):
return False, 'aborted' return False, 'aborted'
# remove symlink
overwrite = True overwrite = True
try: try:
utils.removepath(dst) utils.removepath(dst)
except OSError as exc: except OSError as exc:
err = 'something went wrong with {}: {}'.format(src, exc) err = 'something went wrong with {}: {}'.format(src, exc)
return False, err return False, err
if self.dry: if self.dry:
self.log.dry('would link {} to {}'.format(dst, src)) self.log.dry('would link {} to {}'.format(dst, src))
return True, None return True, None
base = os.path.dirname(dst) base = os.path.dirname(dst)
if not self._create_dirs(base): if not self._create_dirs(base):
err = 'error creating directory for {}'.format(dst) err = 'error creating directory for {}'.format(dst)
return False, err return False, err
# execute pre-actions
ret, err = self._exec_pre_actions(actionexec) ret, err = self._exec_pre_actions(actionexec)
if not ret: if not ret:
return False, err return False, err
# re-check in case action created the file # re-check in case action created the file
if os.path.lexists(dst): if os.path.lexists(dst):
msg = 'Remove "{}" for link creation?'.format(dst) msg = 'Remove "{}" for link creation?'.format(dst)
@@ -395,6 +404,8 @@ class Installer:
except OSError as exc: except OSError as exc:
err = 'something went wrong with {}: {}'.format(src, exc) err = 'something went wrong with {}: {}'.format(src, exc)
return False, err return False, err
# create symlink
os.symlink(src, dst) os.symlink(src, dst)
if not self.comparing: if not self.comparing:
self.log.sub('linked {} to {}'.format(dst, src)) self.log.sub('linked {} to {}'.format(dst, src))
@@ -418,20 +429,17 @@ class Installer:
self.log.dbg('is_template: {}'.format(is_template)) self.log.dbg('is_template: {}'.format(is_template))
self.log.dbg('no empty: {}'.format(noempty)) self.log.dbg('no empty: {}'.format(noempty))
# ignore file
if utils.must_ignore([src, dst], ignore, debug=self.debug):
self.log.dbg('ignoring install of {} to {}'.format(src, dst))
return False, None
# check no loop # check no loop
if utils.samefile(src, dst): if utils.samefile(src, dst):
err = 'dotfile points to itself: {}'.format(dst) err = 'dotfile points to itself: {}'.format(dst)
return False, err return False, err
if utils.must_ignore([src, dst], ignore, debug=self.debug): # check source file exists
self.log.dbg('ignoring install of {} to {}'.format(src, dst))
return False, None
if utils.samefile(src, dst):
# loop
err = 'dotfile points to itself: {}'.format(dst)
return False, err
if not os.path.exists(src): if not os.path.exists(src):
err = 'source dotfile does not exist: {}'.format(src) err = 'source dotfile does not exist: {}'.format(src)
return False, err return False, err
@@ -499,9 +507,9 @@ class Installer:
is_template=is_template) is_template=is_template)
if not res and err: if not res and err:
# error occured # error occured
ret = res, err return res, err
break
elif res: if res:
# something got installed # something got installed
ret = True, None ret = True, None
else: else:
@@ -514,9 +522,9 @@ class Installer:
is_template=is_template) is_template=is_template)
if not res and err: if not res and err:
# error occured # error occured
ret = res, err return res, err
break
elif res: if res:
# something got installed # something got installed
ret = True, None ret = True, None
return ret return ret
@@ -563,6 +571,7 @@ class Installer:
return True, None return True, None
if os.path.lexists(dst): if os.path.lexists(dst):
# file/symlink exists
try: try:
os.stat(dst) os.stat(dst)
except OSError as exc: except OSError as exc:
@@ -575,6 +584,7 @@ class Installer:
if not self._is_different(src, dst, content=content): if not self._is_different(src, dst, content=content):
self.log.dbg('{} is the same'.format(dst)) self.log.dbg('{} is the same'.format(dst))
return False, None return False, None
if self.safe: if self.safe:
self.log.dbg('change detected for {}'.format(dst)) self.log.dbg('change detected for {}'.format(dst))
if self.showdiff: if self.showdiff:
@@ -585,8 +595,8 @@ class Installer:
return False, 'aborted' return False, 'aborted'
overwrite = True overwrite = True
if self.backup and os.path.lexists(dst): if self.backup:
self._backup(dst) self._backup(dst)
# create hierarchy # create hierarchy
base = os.path.dirname(dst) base = os.path.dirname(dst)

View File

@@ -5,6 +5,7 @@ Copyright (c) 2017, deadc0de6
stores all options to use across dotdrop stores all options to use across dotdrop
""" """
# attribute-defined-outside-init
# pylint: disable=W0201 # pylint: disable=W0201
import os import os
@@ -164,7 +165,27 @@ class Options(AttrMonitor):
# start monitoring for bad attribute # start monitoring for bad attribute
self._set_attr_err = True self._set_attr_err = True
# pylint: disable=R0911 @classmethod
def _get_config_from_fs(cls):
"""get config from filesystem"""
# look in ~/.config/dotdrop
cfg = os.path.expanduser(HOMECFG)
path = os.path.join(cfg, CONFIG)
if os.path.exists(path):
return path
# look in /etc/xdg/dotdrop
path = os.path.join(ETCXDGCFG, CONFIG)
if os.path.exists(path):
return path
# look in /etc/dotdrop
path = os.path.join(ETCCFG, CONFIG)
if os.path.exists(path):
return path
return ''
def _get_config_path(self): def _get_config_path(self):
"""get the config path""" """get the config path"""
# cli provided # cli provided
@@ -186,24 +207,7 @@ class Options(AttrMonitor):
if os.path.exists(path): if os.path.exists(path):
return path return path
# look in ~/.config/dotdrop return self._get_config_from_fs()
cfg = os.path.expanduser(HOMECFG)
path = os.path.join(cfg, CONFIG)
if os.path.exists(path):
return path
# look in /etc/xdg/dotdrop
path = os.path.join(ETCXDGCFG, CONFIG)
if os.path.exists(path):
return path
# look in /etc/dotdrop
path = os.path.join(ETCCFG, CONFIG)
if os.path.exists(path):
return path
return ''
# pylint: enable=R0911
def _header(self): def _header(self):
"""display the header""" """display the header"""

View File

@@ -56,7 +56,7 @@ def write_to_tmpfile(content):
return path return path
def shell(cmd, debug=False): def shellrun(cmd, debug=False):
""" """
run a command in the shell (expects a string) run a command in the shell (expects a string)
returns True|False, output returns True|False, output
@@ -256,8 +256,7 @@ def uniq_list(a_list):
def patch_ignores(ignores, prefix, debug=False): def patch_ignores(ignores, prefix, debug=False):
"""allow relative ignore pattern""" """allow relative ignore pattern"""
new = [] new = []
if debug: LOG.dbg('ignores before patching: {}'.format(ignores), force=debug)
LOG.dbg('ignores before patching: {}'.format(ignores), force=True)
for ignore in ignores: for ignore in ignores:
negative = ignore.startswith('!') negative = ignore.startswith('!')
if negative: if negative:
@@ -284,8 +283,7 @@ def patch_ignores(ignores, prefix, debug=False):
new.append('!' + path) new.append('!' + path)
else: else:
new.append(path) new.append(path)
if debug: LOG.dbg('ignores after patching: {}'.format(new), force=debug)
LOG.dbg('ignores after patching: {}'.format(new), force=True)
return new return new
@@ -305,8 +303,12 @@ def get_module_from_path(path):
if not path or not os.path.exists(path): if not path or not os.path.exists(path):
return None return None
module_name = os.path.basename(path).rstrip('.py') module_name = os.path.basename(path).rstrip('.py')
loader = importlib.machinery.SourceFileLoader(module_name, path) # allow any type of files
mod = loader.load_module() importlib.machinery.SOURCE_SUFFIXES.append('')
# import module
spec = importlib.util.spec_from_file_location(module_name, path)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod return mod

View File

@@ -10,8 +10,9 @@ usage example:
./change-link.py --true ../config.yaml --ignore f_vimrc --ignore f_xinitrc ./change-link.py --true ../config.yaml --ignore f_vimrc --ignore f_xinitrc
""" """
from docopt import docopt
import os import os
import io
from docopt import docopt
from ruamel.yaml import YAML as yaml from ruamel.yaml import YAML as yaml
USAGE = """ USAGE = """
@@ -26,11 +27,12 @@ Options:
""" """
key = 'dotfiles' KEY = 'dotfiles'
entry = 'link' ENTRY = 'link'
def main(): def main():
"""entry point"""
args = docopt(USAGE) args = docopt(USAGE)
path = os.path.expanduser(args['<config.yaml>']) path = os.path.expanduser(args['<config.yaml>'])
if args['--true']: if args['--true']:
@@ -40,19 +42,19 @@ def main():
ignores = args['--ignore'] ignores = args['--ignore']
with open(path, 'r') as f: with open(path, 'r') as file:
content = yaml(typ='safe').load(f) content = yaml(typ='safe').load(file)
for k, v in content[key].items(): for k, val in content[KEY].items():
if k in ignores: if k in ignores:
continue continue
v[entry] = value val[ENTRY] = value
output = io.StringIO() output = io.StringIO()
y = yaml() data = yaml()
y.default_flow_style = False data.default_flow_style = False
y.indent = 2 data.indent = 2
y.typ = 'rt' data.typ = 'rt'
y.dump(content, output) data.dump(content, output)
print(output) print(output)