mirror of
https://github.com/deadc0de6/dotdrop.git
synced 2026-02-04 14:31:46 +00:00
linting
This commit is contained in:
@@ -3,6 +3,7 @@ author: deadc0de6 (https://github.com/deadc0de6)
|
||||
Copyright (c) 2017, deadc0de6
|
||||
"""
|
||||
|
||||
# pylint: disable=C0415
|
||||
import sys
|
||||
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ from dotdrop.exceptions import YamlException, UndefinedException
|
||||
|
||||
|
||||
class CfgYaml:
|
||||
"""yaml config file parser"""
|
||||
|
||||
# global entries
|
||||
key_settings = Settings.key_yaml
|
||||
@@ -437,7 +438,6 @@ class CfgYaml:
|
||||
self._log.warn(warn)
|
||||
|
||||
self._dirty = False
|
||||
self.cfg_updated = False
|
||||
return True
|
||||
|
||||
def dump(self):
|
||||
@@ -464,20 +464,20 @@ class CfgYaml:
|
||||
self._check_minversion(minversion)
|
||||
|
||||
# normalize paths
|
||||
p = self._norm_path(settings[self.key_settings_dotpath])
|
||||
settings[self.key_settings_dotpath] = p
|
||||
p = self._norm_path(settings[self.key_settings_workdir])
|
||||
settings[self.key_settings_workdir] = p
|
||||
p = [
|
||||
self._norm_path(p)
|
||||
for p in settings[Settings.key_filter_file]
|
||||
paths = self._norm_path(settings[self.key_settings_dotpath])
|
||||
settings[self.key_settings_dotpath] = paths
|
||||
paths = self._norm_path(settings[self.key_settings_workdir])
|
||||
settings[self.key_settings_workdir] = paths
|
||||
paths = [
|
||||
self._norm_path(path)
|
||||
for path in settings[Settings.key_filter_file]
|
||||
]
|
||||
settings[Settings.key_filter_file] = p
|
||||
p = [
|
||||
self._norm_path(p)
|
||||
for p in settings[Settings.key_func_file]
|
||||
settings[Settings.key_filter_file] = paths
|
||||
paths = [
|
||||
self._norm_path(path)
|
||||
for path in settings[Settings.key_func_file]
|
||||
]
|
||||
settings[Settings.key_func_file] = p
|
||||
settings[Settings.key_func_file] = paths
|
||||
if self._debug:
|
||||
self._debug_dict('settings block:', settings)
|
||||
return settings
|
||||
@@ -1166,11 +1166,11 @@ class CfgYaml:
|
||||
new = []
|
||||
if not entries:
|
||||
return new
|
||||
for e in entries:
|
||||
et = self._template_item(e)
|
||||
if self._debug and e != et:
|
||||
self._dbg('resolved: {} -> {}'.format(e, et))
|
||||
new.append(et)
|
||||
for entry in entries:
|
||||
newe = self._template_item(entry)
|
||||
if self._debug and entry != newe:
|
||||
self._dbg('resolved: {} -> {}'.format(entry, newe))
|
||||
new.append(newe)
|
||||
return new
|
||||
|
||||
def _template_dict(self, entries):
|
||||
@@ -1178,11 +1178,11 @@ class CfgYaml:
|
||||
new = {}
|
||||
if not entries:
|
||||
return new
|
||||
for k, v in entries.items():
|
||||
vt = self._template_item(v)
|
||||
if self._debug and v != vt:
|
||||
self._dbg('resolved: {} -> {}'.format(v, vt))
|
||||
new[k] = vt
|
||||
for k, val in entries.items():
|
||||
newv = self._template_item(val)
|
||||
if self._debug and val != newv:
|
||||
self._dbg('resolved: {} -> {}'.format(val, newv))
|
||||
new[k] = newv
|
||||
return new
|
||||
|
||||
def _template_dotfiles_entries(self):
|
||||
@@ -1226,9 +1226,9 @@ class CfgYaml:
|
||||
if self.key_all not in pdfs:
|
||||
# take a subset of the dotfiles
|
||||
newdotfiles = {}
|
||||
for k, v in dotfiles.items():
|
||||
for k, val in dotfiles.items():
|
||||
if k in pdfs:
|
||||
newdotfiles[k] = v
|
||||
newdotfiles[k] = val
|
||||
dotfiles = newdotfiles
|
||||
|
||||
for dotfile in dotfiles.values():
|
||||
@@ -1246,26 +1246,29 @@ class CfgYaml:
|
||||
var = self._enrich_vars(variables, self._profile)
|
||||
# use a separated templategen to handle variables
|
||||
# resolved outside the main config
|
||||
t = Templategen(variables=var,
|
||||
func_file=self.settings[Settings.key_func_file],
|
||||
filter_file=self.settings[Settings.key_filter_file])
|
||||
func_files = self.settings[Settings.key_func_file]
|
||||
filter_files = self.settings[Settings.key_filter_file]
|
||||
templ = Templategen(variables=var,
|
||||
func_file=func_files,
|
||||
filter_file=filter_files)
|
||||
for k in variables.keys():
|
||||
val = variables[k]
|
||||
while Templategen.var_is_template(val):
|
||||
val = t.generate_string(val)
|
||||
val = templ.generate_string(val)
|
||||
variables[k] = val
|
||||
t.update_variables(variables)
|
||||
templ.update_variables(variables)
|
||||
if variables is self.variables:
|
||||
self._redefine_templater()
|
||||
|
||||
def _get_profile_included_vars(self):
|
||||
"""resolve profile included variables/dynvariables"""
|
||||
for k, v in self.profiles.items():
|
||||
if self.key_profile_include in v and v[self.key_profile_include]:
|
||||
for _, val in self.profiles.items():
|
||||
if self.key_profile_include in val and \
|
||||
val[self.key_profile_include]:
|
||||
new = []
|
||||
for x in v[self.key_profile_include]:
|
||||
new.append(self._tmpl.generate_string(x))
|
||||
v[self.key_profile_include] = new
|
||||
for entry in val[self.key_profile_include]:
|
||||
new.append(self._tmpl.generate_string(entry))
|
||||
val[self.key_profile_include] = new
|
||||
|
||||
# now get the included ones
|
||||
pro_var = self._get_profile_included_item(self.key_profile_variables)
|
||||
@@ -1291,7 +1294,8 @@ class CfgYaml:
|
||||
"""
|
||||
if not dic:
|
||||
return
|
||||
[dic.pop(k, None) for k in self._profilevarskeys]
|
||||
for k in self._profilevarskeys:
|
||||
dic.pop(k, None)
|
||||
|
||||
def _parse_extended_import_path(self, path_entry):
|
||||
"""Parse an import path in a tuple (path, fatal_not_found)."""
|
||||
@@ -1369,7 +1373,8 @@ class CfgYaml:
|
||||
processed_paths = (self._process_path(p) for p in paths)
|
||||
return list(chain.from_iterable(processed_paths))
|
||||
|
||||
def _merge_dict(self, high, low):
|
||||
@classmethod
|
||||
def _merge_dict(cls, high, low):
|
||||
"""merge high and low dict"""
|
||||
if not high:
|
||||
high = {}
|
||||
@@ -1377,7 +1382,8 @@ class CfgYaml:
|
||||
low = {}
|
||||
return {**low, **high}
|
||||
|
||||
def _get_entry(self, dic, key, mandatory=True):
|
||||
@classmethod
|
||||
def _get_entry(cls, dic, key, mandatory=True):
|
||||
"""return copy of entry from yaml dictionary"""
|
||||
if key not in dic:
|
||||
if mandatory:
|
||||
@@ -1393,19 +1399,19 @@ class CfgYaml:
|
||||
def _clear_none(self, dic):
|
||||
"""recursively delete all none/empty values in a dictionary."""
|
||||
new = {}
|
||||
for k, v in dic.items():
|
||||
for k, val in dic.items():
|
||||
if k == self.key_dotfile_src:
|
||||
# allow empty dotfile src
|
||||
new[k] = v
|
||||
new[k] = val
|
||||
continue
|
||||
if k == self.key_dotfile_dst:
|
||||
# allow empty dotfile dst
|
||||
new[k] = v
|
||||
new[k] = val
|
||||
continue
|
||||
newv = v
|
||||
if isinstance(v, dict):
|
||||
newv = val
|
||||
if isinstance(val, dict):
|
||||
# recursive travers dict
|
||||
newv = self._clear_none(v)
|
||||
newv = self._clear_none(val)
|
||||
if not newv:
|
||||
# no empty dict
|
||||
continue
|
||||
@@ -1418,7 +1424,8 @@ class CfgYaml:
|
||||
new[k] = newv
|
||||
return new
|
||||
|
||||
def _is_glob(self, path):
|
||||
@classmethod
|
||||
def _is_glob(cls, path):
|
||||
"""Quick test if path is a glob."""
|
||||
return '*' in path or '?' in path
|
||||
|
||||
@@ -1435,8 +1442,8 @@ class CfgYaml:
|
||||
return path
|
||||
path = os.path.expanduser(path)
|
||||
if not os.path.isabs(path):
|
||||
d = os.path.dirname(self._path)
|
||||
ret = os.path.join(d, path)
|
||||
dirn = os.path.dirname(self._path)
|
||||
ret = os.path.join(dirn, path)
|
||||
if self._debug:
|
||||
msg = 'normalizing relative to cfg: {} -> {}'
|
||||
self._dbg(msg.format(path, ret))
|
||||
@@ -1446,30 +1453,31 @@ class CfgYaml:
|
||||
self._dbg('normalizing: {} -> {}'.format(path, ret))
|
||||
return ret
|
||||
|
||||
def _shell_exec_dvars(self, dic, keys=[]):
|
||||
def _shell_exec_dvars(self, dic, keys=None):
|
||||
"""shell execute dynvariables in-place"""
|
||||
if not keys:
|
||||
keys = dic.keys()
|
||||
for k in keys:
|
||||
v = dic[k]
|
||||
ret, out = shell(v, debug=self._debug)
|
||||
val = dic[k]
|
||||
ret, out = shell(val, debug=self._debug)
|
||||
if not ret:
|
||||
err = 'var \"{}: {}\" failed: {}'.format(k, v, out)
|
||||
err = 'var \"{}: {}\" failed: {}'.format(k, val, out)
|
||||
self._log.err(err)
|
||||
raise YamlException(err)
|
||||
if self._debug:
|
||||
self._dbg('{}: `{}` -> {}'.format(k, v, out))
|
||||
self._dbg('{}: `{}` -> {}'.format(k, val, out))
|
||||
dic[k] = out
|
||||
|
||||
def _check_minversion(self, minversion):
|
||||
@classmethod
|
||||
def _check_minversion(cls, minversion):
|
||||
if not minversion:
|
||||
return
|
||||
try:
|
||||
cur = tuple([int(x) for x in VERSION.split('.')])
|
||||
cfg = tuple([int(x) for x in minversion.split('.')])
|
||||
except Exception:
|
||||
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)
|
||||
raise YamlException(err)
|
||||
raise YamlException(err) from exc
|
||||
if cur < cfg:
|
||||
err = 'current dotdrop version is too old for that config file.'
|
||||
err += ' Please update.'
|
||||
@@ -1495,8 +1503,8 @@ class CfgYaml:
|
||||
self._dbg('{}:'.format(title))
|
||||
if not elems:
|
||||
return
|
||||
for k, v in elems.items():
|
||||
self._dbg('\t- \"{}\": {}'.format(k, v))
|
||||
for k, val in elems.items():
|
||||
self._dbg('\t- \"{}\": {}'.format(k, val))
|
||||
|
||||
def _dbg(self, content):
|
||||
pre = os.path.basename(self._path)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,3 +12,7 @@ class YamlException(Exception):
|
||||
|
||||
class UndefinedException(Exception):
|
||||
"""exception in templating"""
|
||||
|
||||
|
||||
class UnmetDependency(Exception):
|
||||
"""unmet dependency"""
|
||||
|
||||
@@ -17,6 +17,7 @@ from dotdrop.exceptions import UndefinedException
|
||||
|
||||
|
||||
class Installer:
|
||||
"""dotfile installer"""
|
||||
|
||||
def __init__(self, base='.', create=True, backup=True,
|
||||
dry=False, safe=False, workdir='~/.config/dotdrop',
|
||||
@@ -65,7 +66,7 @@ class Installer:
|
||||
|
||||
def install(self, templater, src, dst, linktype,
|
||||
actionexec=None, noempty=False,
|
||||
ignore=[], is_template=True,
|
||||
ignore=None, is_template=True,
|
||||
chmod=None, force_chmod=False):
|
||||
"""
|
||||
install src to dst
|
||||
@@ -93,7 +94,7 @@ class Installer:
|
||||
return True, None
|
||||
msg = 'installing \"{}\" to \"{}\" (link: {})'
|
||||
self.log.dbg(msg.format(src, dst, str(linktype)))
|
||||
src, dst, cont, err = self._check_paths(src, dst, chmod)
|
||||
src, dst, cont, err = self._check_paths(src, dst)
|
||||
if not cont:
|
||||
return self._log_install(cont, err)
|
||||
|
||||
@@ -108,10 +109,13 @@ class Installer:
|
||||
# install to temporary dir
|
||||
# and ignore any actions
|
||||
if self.totemp:
|
||||
r, err, _ = self.install_to_temp(templater, self.totemp,
|
||||
src, dst, is_template=is_template,
|
||||
chmod=chmod, ignore=ignore)
|
||||
return self._log_install(r, err)
|
||||
ret, err, _ = self.install_to_temp(templater,
|
||||
self.totemp,
|
||||
src, dst,
|
||||
is_template=is_template,
|
||||
chmod=chmod,
|
||||
ignore=ignore)
|
||||
return self._log_install(ret, err)
|
||||
|
||||
isdir = os.path.isdir(src)
|
||||
self.log.dbg('install {} to {}'.format(src, dst))
|
||||
@@ -120,39 +124,37 @@ class Installer:
|
||||
if linktype == LinkTypes.NOLINK:
|
||||
# normal file
|
||||
if isdir:
|
||||
r, err = self._copy_dir(templater, src, dst,
|
||||
actionexec=actionexec,
|
||||
noempty=noempty, ignore=ignore,
|
||||
is_template=is_template,
|
||||
chmod=chmod)
|
||||
ret, err = self._copy_dir(templater, src, dst,
|
||||
actionexec=actionexec,
|
||||
noempty=noempty, ignore=ignore,
|
||||
is_template=is_template)
|
||||
else:
|
||||
r, err = self._copy_file(templater, src, dst,
|
||||
actionexec=actionexec,
|
||||
noempty=noempty, ignore=ignore,
|
||||
is_template=is_template,
|
||||
chmod=chmod)
|
||||
ret, err = self._copy_file(templater, src, dst,
|
||||
actionexec=actionexec,
|
||||
noempty=noempty, ignore=ignore,
|
||||
is_template=is_template)
|
||||
elif linktype == LinkTypes.LINK:
|
||||
# symlink
|
||||
r, err = self._link(templater, src, dst,
|
||||
actionexec=actionexec,
|
||||
is_template=is_template)
|
||||
ret, err = self._link(templater, src, dst,
|
||||
actionexec=actionexec,
|
||||
is_template=is_template)
|
||||
elif linktype == LinkTypes.LINK_CHILDREN:
|
||||
# symlink direct children
|
||||
if not isdir:
|
||||
msg = 'symlink children of {} to {}'
|
||||
self.log.dbg(msg.format(src, dst))
|
||||
err = 'source dotfile is not a directory: {}'.format(src)
|
||||
r = False
|
||||
ret = False
|
||||
else:
|
||||
r, err = self._link_children(templater, src, dst,
|
||||
actionexec=actionexec,
|
||||
is_template=is_template,
|
||||
ignore=ignore)
|
||||
ret, err = self._link_children(templater, src, dst,
|
||||
actionexec=actionexec,
|
||||
is_template=is_template,
|
||||
ignore=ignore)
|
||||
|
||||
self.log.dbg('before chmod: {} err:{}'.format(r, err))
|
||||
self.log.dbg('before chmod: {} err:{}'.format(ret, err))
|
||||
|
||||
if self.dry:
|
||||
return self._log_install(r, err)
|
||||
return self._log_install(ret, err)
|
||||
|
||||
# handle chmod
|
||||
# - on success (r, not err)
|
||||
@@ -160,7 +162,7 @@ class Installer:
|
||||
# but not when
|
||||
# - error (not r, err)
|
||||
# - aborted (not r, err)
|
||||
if (r or (not r and not err)):
|
||||
if (ret or (not ret and not err)):
|
||||
if not chmod:
|
||||
chmod = utils.get_file_perm(src)
|
||||
dstperms = utils.get_file_perm(dst)
|
||||
@@ -168,21 +170,21 @@ class Installer:
|
||||
# apply mode
|
||||
msg = 'chmod {} to {:o}'.format(dst, chmod)
|
||||
if not force_chmod and self.safe and not self.log.ask(msg):
|
||||
r = False
|
||||
ret = False
|
||||
err = 'aborted'
|
||||
else:
|
||||
if not self.comparing:
|
||||
self.log.sub('chmod {} to {:o}'.format(dst, chmod))
|
||||
if utils.chmod(dst, chmod, debug=self.debug):
|
||||
r = True
|
||||
ret = True
|
||||
else:
|
||||
r = False
|
||||
ret = False
|
||||
err = 'chmod failed'
|
||||
|
||||
return self._log_install(r, err)
|
||||
return self._log_install(ret, err)
|
||||
|
||||
def install_to_temp(self, templater, tmpdir, src, dst,
|
||||
is_template=True, chmod=None, ignore=[]):
|
||||
is_template=True, chmod=None, ignore=None):
|
||||
"""
|
||||
install a dotfile to a tempdir
|
||||
|
||||
@@ -198,9 +200,10 @@ class Installer:
|
||||
- success, error-if-any, dotfile-installed-path
|
||||
"""
|
||||
self.log.dbg('tmp install {} (defined dst: {})'.format(src, dst))
|
||||
src, dst, cont, err = self._check_paths(src, dst, chmod)
|
||||
src, dst, cont, err = self._check_paths(src, dst)
|
||||
if not cont:
|
||||
return self._log_install(cont, err)
|
||||
self._log_install(cont, err)
|
||||
return cont, err, None
|
||||
|
||||
ret = False
|
||||
tmpdst = ''
|
||||
@@ -253,18 +256,18 @@ class Installer:
|
||||
self.log.dbg('is a template')
|
||||
self.log.dbg('install to {}'.format(self.workdir))
|
||||
tmp = self._pivot_path(dst, self.workdir, striphome=True)
|
||||
r, err = self.install(templater, src, tmp,
|
||||
LinkTypes.NOLINK,
|
||||
actionexec=actionexec,
|
||||
is_template=is_template)
|
||||
if not r and not os.path.exists(tmp):
|
||||
return r, err
|
||||
ret, err = self.install(templater, src, tmp,
|
||||
LinkTypes.NOLINK,
|
||||
actionexec=actionexec,
|
||||
is_template=is_template)
|
||||
if not ret and not os.path.exists(tmp):
|
||||
return ret, err
|
||||
src = tmp
|
||||
r, err = self._symlink(src, dst, actionexec=actionexec)
|
||||
return r, err
|
||||
ret, err = self._symlink(src, dst, actionexec=actionexec)
|
||||
return ret, err
|
||||
|
||||
def _link_children(self, templater, src, dst,
|
||||
actionexec=None, is_template=True, ignore=[]):
|
||||
actionexec=None, is_template=True, ignore=None):
|
||||
"""
|
||||
install link:link_children
|
||||
|
||||
@@ -318,11 +321,11 @@ class Installer:
|
||||
self.log.dbg('install to {} and symlink'
|
||||
.format(self.workdir))
|
||||
tmp = self._pivot_path(subdst, self.workdir, striphome=True)
|
||||
r, e = self.install(templater, subsrc, tmp,
|
||||
LinkTypes.NOLINK,
|
||||
actionexec=actionexec,
|
||||
is_template=is_template)
|
||||
if not r and e and not os.path.exists(tmp):
|
||||
ret2, err2 = self.install(templater, subsrc, tmp,
|
||||
LinkTypes.NOLINK,
|
||||
actionexec=actionexec,
|
||||
is_template=is_template)
|
||||
if not ret2 and err2 and not os.path.exists(tmp):
|
||||
continue
|
||||
subsrc = tmp
|
||||
|
||||
@@ -369,8 +372,8 @@ class Installer:
|
||||
overwrite = True
|
||||
try:
|
||||
utils.removepath(dst)
|
||||
except OSError as e:
|
||||
err = 'something went wrong with {}: {}'.format(src, e)
|
||||
except OSError as exc:
|
||||
err = 'something went wrong with {}: {}'.format(src, exc)
|
||||
return False, err
|
||||
if self.dry:
|
||||
self.log.dry('would link {} to {}'.format(dst, src))
|
||||
@@ -379,9 +382,9 @@ class Installer:
|
||||
if not self._create_dirs(base):
|
||||
err = 'error creating directory for {}'.format(dst)
|
||||
return False, err
|
||||
r, e = self._exec_pre_actions(actionexec)
|
||||
if not r:
|
||||
return False, e
|
||||
ret, err = self._exec_pre_actions(actionexec)
|
||||
if not ret:
|
||||
return False, err
|
||||
# re-check in case action created the file
|
||||
if os.path.lexists(dst):
|
||||
msg = 'Remove "{}" for link creation?'.format(dst)
|
||||
@@ -389,8 +392,8 @@ class Installer:
|
||||
return False, 'aborted'
|
||||
try:
|
||||
utils.removepath(dst)
|
||||
except OSError as e:
|
||||
err = 'something went wrong with {}: {}'.format(src, e)
|
||||
except OSError as exc:
|
||||
err = 'something went wrong with {}: {}'.format(src, exc)
|
||||
return False, err
|
||||
os.symlink(src, dst)
|
||||
if not self.comparing:
|
||||
@@ -399,8 +402,7 @@ class Installer:
|
||||
|
||||
def _copy_file(self, templater, src, dst,
|
||||
actionexec=None, noempty=False,
|
||||
ignore=[], is_template=True,
|
||||
chmod=None):
|
||||
ignore=None, is_template=True):
|
||||
"""
|
||||
install src to dst when is a file
|
||||
|
||||
@@ -441,8 +443,8 @@ class Installer:
|
||||
saved = templater.add_tmp_vars(self._get_tmp_file_vars(src, dst))
|
||||
try:
|
||||
content = templater.generate(src)
|
||||
except UndefinedException as e:
|
||||
return False, str(e)
|
||||
except UndefinedException as exc:
|
||||
return False, str(exc)
|
||||
finally:
|
||||
templater.restore_vars(saved)
|
||||
# test is empty
|
||||
@@ -456,8 +458,7 @@ class Installer:
|
||||
# write the file
|
||||
ret, err = self._write(src, dst,
|
||||
content=content,
|
||||
actionexec=actionexec,
|
||||
chmod=chmod)
|
||||
actionexec=actionexec)
|
||||
if ret and not err:
|
||||
if not self.dry and not self.comparing:
|
||||
self.log.sub('install {} to {}'.format(src, dst))
|
||||
@@ -465,7 +466,7 @@ class Installer:
|
||||
|
||||
def _copy_dir(self, templater, src, dst,
|
||||
actionexec=None, noempty=False,
|
||||
ignore=[], is_template=True, chmod=None):
|
||||
ignore=None, is_template=True):
|
||||
"""
|
||||
install src to dst when is a directory
|
||||
|
||||
@@ -486,17 +487,16 @@ class Installer:
|
||||
|
||||
# handle all files in dir
|
||||
for entry in os.listdir(src):
|
||||
f = os.path.join(src, entry)
|
||||
fpath = os.path.join(src, entry)
|
||||
self.log.dbg('deploy sub from {}: {}'.format(dst, entry))
|
||||
if not os.path.isdir(f):
|
||||
if not os.path.isdir(fpath):
|
||||
# is file
|
||||
res, err = self._copy_file(templater, f,
|
||||
res, err = self._copy_file(templater, fpath,
|
||||
os.path.join(dst, entry),
|
||||
actionexec=actionexec,
|
||||
noempty=noempty,
|
||||
ignore=ignore,
|
||||
is_template=is_template,
|
||||
chmod=None)
|
||||
is_template=is_template)
|
||||
if not res and err:
|
||||
# error occured
|
||||
ret = res, err
|
||||
@@ -506,13 +506,12 @@ class Installer:
|
||||
ret = True, None
|
||||
else:
|
||||
# is directory
|
||||
res, err = self._copy_dir(templater, f,
|
||||
res, err = self._copy_dir(templater, fpath,
|
||||
os.path.join(dst, entry),
|
||||
actionexec=actionexec,
|
||||
noempty=noempty,
|
||||
ignore=ignore,
|
||||
is_template=is_template,
|
||||
chmod=None)
|
||||
is_template=is_template)
|
||||
if not res and err:
|
||||
# error occured
|
||||
ret = res, err
|
||||
@@ -522,8 +521,33 @@ class Installer:
|
||||
ret = True, None
|
||||
return ret
|
||||
|
||||
@classmethod
|
||||
def _write_content_to_file(cls, content, src, dst):
|
||||
"""write content to file"""
|
||||
if content:
|
||||
# write content the file
|
||||
try:
|
||||
with open(dst, 'wb') as file:
|
||||
file.write(content)
|
||||
shutil.copymode(src, dst)
|
||||
except NotADirectoryError as exc:
|
||||
err = 'opening dest file: {}'.format(exc)
|
||||
return False, err
|
||||
except OSError as exc:
|
||||
return False, str(exc)
|
||||
except TypeError as exc:
|
||||
return False, str(exc)
|
||||
else:
|
||||
# copy file
|
||||
try:
|
||||
shutil.copyfile(src, dst)
|
||||
shutil.copymode(src, dst)
|
||||
except OSError as exc:
|
||||
return False, str(exc)
|
||||
return True, None
|
||||
|
||||
def _write(self, src, dst, content=None,
|
||||
actionexec=None, chmod=None):
|
||||
actionexec=None):
|
||||
"""
|
||||
copy dotfile / write content to file
|
||||
|
||||
@@ -541,8 +565,8 @@ class Installer:
|
||||
if os.path.lexists(dst):
|
||||
try:
|
||||
os.stat(dst)
|
||||
except OSError as e:
|
||||
if e.errno == errno.ENOENT:
|
||||
except OSError as exc:
|
||||
if exc.errno == errno.ENOENT:
|
||||
# broken symlink
|
||||
err = 'broken symlink {}'.format(dst)
|
||||
return False, err
|
||||
@@ -560,47 +584,38 @@ class Installer:
|
||||
if not self.log.ask('Overwrite \"{}\"'.format(dst)):
|
||||
return False, 'aborted'
|
||||
overwrite = True
|
||||
|
||||
if self.backup and os.path.lexists(dst):
|
||||
self._backup(dst)
|
||||
|
||||
# create hierarchy
|
||||
base = os.path.dirname(dst)
|
||||
if not self._create_dirs(base):
|
||||
err = 'creating directory for {}'.format(dst)
|
||||
return False, err
|
||||
r, e = self._exec_pre_actions(actionexec)
|
||||
if not r:
|
||||
return False, e
|
||||
|
||||
# execute pre actions
|
||||
ret, err = self._exec_pre_actions(actionexec)
|
||||
if not ret:
|
||||
return False, err
|
||||
|
||||
self.log.dbg('install file 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)):
|
||||
self.log.warn('ignoring {}'.format(dst))
|
||||
return False, 'aborted'
|
||||
if self.safe and not overwrite and \
|
||||
os.path.lexists(dst) and \
|
||||
not self.log.ask('Overwrite \"{}\"'.format(dst)):
|
||||
self.log.warn('ignoring {}'.format(dst))
|
||||
return False, 'aborted'
|
||||
|
||||
if content:
|
||||
# write content the file
|
||||
try:
|
||||
with open(dst, 'wb') as f:
|
||||
f.write(content)
|
||||
shutil.copymode(src, dst)
|
||||
except NotADirectoryError as e:
|
||||
err = 'opening dest file: {}'.format(e)
|
||||
return False, err
|
||||
except Exception as e:
|
||||
return False, str(e)
|
||||
else:
|
||||
# copy file
|
||||
try:
|
||||
shutil.copyfile(src, dst)
|
||||
shutil.copymode(src, dst)
|
||||
except Exception as e:
|
||||
return False, str(e)
|
||||
return True, None
|
||||
# writing to file
|
||||
return self._write_content_to_file(content, src, dst)
|
||||
|
||||
########################################################
|
||||
# helpers
|
||||
########################################################
|
||||
|
||||
def _get_tmp_file_vars(self, src, dst):
|
||||
@classmethod
|
||||
def _get_tmp_file_vars(cls, src, dst):
|
||||
tmp = {}
|
||||
tmp['_dotfile_sub_abs_src'] = src
|
||||
tmp['_dotfile_sub_abs_dst'] = dst
|
||||
@@ -615,10 +630,10 @@ class Installer:
|
||||
if content:
|
||||
tmp = utils.write_to_tmpfile(content)
|
||||
src = tmp
|
||||
r = utils.fastdiff(src, dst)
|
||||
if r:
|
||||
ret = utils.fastdiff(src, dst)
|
||||
if ret:
|
||||
self.log.dbg('content differ')
|
||||
return r
|
||||
return ret
|
||||
|
||||
def _show_diff_before_write(self, src, dst, content=None):
|
||||
"""
|
||||
@@ -691,7 +706,10 @@ class Installer:
|
||||
return ret, err
|
||||
|
||||
def _log_install(self, boolean, err):
|
||||
"""log installation process"""
|
||||
"""
|
||||
log installation process
|
||||
returns success, error-if-any
|
||||
"""
|
||||
if not self.debug:
|
||||
return boolean, err
|
||||
if boolean:
|
||||
@@ -703,7 +721,7 @@ class Installer:
|
||||
self.log.dbg('install: IGNORED')
|
||||
return boolean, err
|
||||
|
||||
def _check_paths(self, src, dst, chmod):
|
||||
def _check_paths(self, src, dst):
|
||||
"""
|
||||
check and normalize param
|
||||
returns <src>, <dst>, <continue>, <error>
|
||||
|
||||
@@ -30,9 +30,10 @@ COMMENT_END = '@@#}'
|
||||
|
||||
|
||||
class Templategen:
|
||||
"""dotfile templater"""
|
||||
|
||||
def __init__(self, base='.', variables={},
|
||||
func_file=[], filter_file=[], debug=False):
|
||||
def __init__(self, base='.', variables=None,
|
||||
func_file=None, filter_file=None, debug=False):
|
||||
"""constructor
|
||||
@base: directory path where to search for templates
|
||||
@variables: dictionary of variables for templates
|
||||
@@ -69,13 +70,13 @@ class Templategen:
|
||||
self.log.dbg('load global functions:')
|
||||
self._load_funcs_to_dic(jhelpers, self.env.globals)
|
||||
if func_file:
|
||||
for f in func_file:
|
||||
self.log.dbg('load custom functions from {}'.format(f))
|
||||
self._load_path_to_dic(f, self.env.globals)
|
||||
for ffile in func_file:
|
||||
self.log.dbg('load custom functions from {}'.format(ffile))
|
||||
self._load_path_to_dic(ffile, self.env.globals)
|
||||
if filter_file:
|
||||
for f in filter_file:
|
||||
self.log.dbg('load custom filters from {}'.format(f))
|
||||
self._load_path_to_dic(f, self.env.filters)
|
||||
for ffile in filter_file:
|
||||
self.log.dbg('load custom filters from {}'.format(ffile))
|
||||
self._load_path_to_dic(ffile, self.env.filters)
|
||||
if self.debug:
|
||||
self._debug_dict('template additional variables', variables)
|
||||
|
||||
@@ -89,9 +90,9 @@ class Templategen:
|
||||
return ''
|
||||
try:
|
||||
return self._handle_file(src)
|
||||
except UndefinedError as e:
|
||||
err = 'undefined variable: {}'.format(e.message)
|
||||
raise UndefinedException(err)
|
||||
except UndefinedError as exc:
|
||||
err = 'undefined variable: {}'.format(exc.message)
|
||||
raise UndefinedException(err) from exc
|
||||
|
||||
def generate_string(self, string):
|
||||
"""
|
||||
@@ -103,11 +104,11 @@ class Templategen:
|
||||
return ''
|
||||
try:
|
||||
return self.env.from_string(string).render(self.variables)
|
||||
except UndefinedError as e:
|
||||
err = 'undefined variable: {}'.format(e.message)
|
||||
raise UndefinedException(err)
|
||||
except UndefinedError as exc:
|
||||
err = 'undefined variable: {}'.format(exc.message)
|
||||
raise UndefinedException(err) from exc
|
||||
|
||||
def add_tmp_vars(self, newvars={}):
|
||||
def add_tmp_vars(self, newvars=None):
|
||||
"""add vars to the globals, make sure to call restore_vars"""
|
||||
saved_variables = self.variables.copy()
|
||||
if not newvars:
|
||||
@@ -139,13 +140,15 @@ class Templategen:
|
||||
self.log.dbg('load function \"{}\"'.format(name))
|
||||
dic[name] = func
|
||||
|
||||
def _header(self, prepend=''):
|
||||
@classmethod
|
||||
def _header(cls, prepend=''):
|
||||
"""add a comment usually in the header of a dotfile"""
|
||||
return '{}{}'.format(prepend, utils.header())
|
||||
|
||||
def _handle_file(self, src):
|
||||
"""generate the file content from template"""
|
||||
try:
|
||||
# pylint: disable=C0415
|
||||
import magic
|
||||
filetype = magic.from_file(src, mime=True)
|
||||
self.log.dbg('using \"magic\" for filetype identification')
|
||||
@@ -162,7 +165,8 @@ class Templategen:
|
||||
return self._handle_bin_file(src)
|
||||
return self._handle_text_file(src)
|
||||
|
||||
def _is_text(self, fileoutput):
|
||||
@classmethod
|
||||
def _is_text(cls, fileoutput):
|
||||
"""return if `file -b` output is ascii text"""
|
||||
out = fileoutput.lower()
|
||||
if out.startswith('text'):
|
||||
@@ -179,8 +183,8 @@ class Templategen:
|
||||
path = os.path.normpath(path)
|
||||
if not os.path.exists(path):
|
||||
raise TemplateNotFound(path)
|
||||
with open(path, 'r') as f:
|
||||
content = f.read()
|
||||
with open(path, 'r') as file:
|
||||
content = file.read()
|
||||
return content
|
||||
|
||||
def _handle_text_file(self, src):
|
||||
@@ -199,18 +203,19 @@ class Templategen:
|
||||
# this is dirty
|
||||
if not src.startswith(self.base):
|
||||
src = os.path.join(self.base, src)
|
||||
with open(src, 'rb') as f:
|
||||
content = f.read()
|
||||
with open(src, 'rb') as file:
|
||||
content = file.read()
|
||||
return content
|
||||
|
||||
def _read_bad_encoded_text(self, path):
|
||||
@classmethod
|
||||
def _read_bad_encoded_text(cls, path):
|
||||
"""decode non utf-8 data"""
|
||||
with open(path, 'rb') as f:
|
||||
data = f.read()
|
||||
with open(path, 'rb') as file:
|
||||
data = file.read()
|
||||
return data.decode('utf-8', 'replace')
|
||||
|
||||
@staticmethod
|
||||
def is_template(path, ignore=[]):
|
||||
def is_template(path, ignore=None):
|
||||
"""recursively check if any file is a template within path"""
|
||||
path = os.path.expanduser(path)
|
||||
|
||||
@@ -250,10 +255,11 @@ class Templategen:
|
||||
markers = [BLOCK_START, VAR_START, COMMENT_START]
|
||||
patterns = [re.compile(marker.encode()) for marker in markers]
|
||||
try:
|
||||
with io.open(path, "r", encoding="utf-8") as f:
|
||||
m = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
|
||||
with io.open(path, "r", encoding="utf-8") as file:
|
||||
mapf = mmap.mmap(file.fileno(), 0,
|
||||
access=mmap.ACCESS_READ)
|
||||
for pattern in patterns:
|
||||
if pattern.search(m):
|
||||
if pattern.search(mapf):
|
||||
return True
|
||||
except UnicodeDecodeError:
|
||||
# is binary so surely no template
|
||||
@@ -267,5 +273,5 @@ class Templategen:
|
||||
self.log.dbg('{}:'.format(title))
|
||||
if not elems:
|
||||
return
|
||||
for k, v in elems.items():
|
||||
self.log.dbg(' - \"{}\": {}'.format(k, v))
|
||||
for k, val in elems.items():
|
||||
self.log.dbg(' - \"{}\": {}'.format(k, val))
|
||||
|
||||
@@ -18,6 +18,7 @@ from shutil import rmtree, which
|
||||
|
||||
# local import
|
||||
from dotdrop.logger import Logger
|
||||
from dotdrop.exceptions import UnmetDependency
|
||||
|
||||
LOG = Logger()
|
||||
STAR = '*'
|
||||
@@ -316,7 +317,7 @@ def dependencies_met():
|
||||
err = 'The tool \"{}\" was not found in the PATH!'
|
||||
for dep in deps:
|
||||
if not which(dep):
|
||||
raise Exception(err.format(dep))
|
||||
raise UnmetDependency(err.format(dep))
|
||||
# check python deps
|
||||
err = 'missing python module \"{}\"'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user