mirror of
https://github.com/deadc0de6/dotdrop.git
synced 2026-02-09 10:39:17 +00:00
refactoring cfg_yaml
This commit is contained in:
@@ -104,14 +104,18 @@ class CfgYaml:
|
|||||||
# 3) "import_configs" variables
|
# 3) "import_configs" variables
|
||||||
# 4) other variables
|
# 4) other variables
|
||||||
#
|
#
|
||||||
|
# variables / dynvariables hints
|
||||||
|
# - dynvariables are executed in their own config file
|
||||||
|
#
|
||||||
# parse a config file
|
# parse a config file
|
||||||
# - parse settings
|
# - parse settings
|
||||||
# - parse variables
|
# - parse variables
|
||||||
# - interprete dynvariables
|
# - interprete dynvariables
|
||||||
# - template the include entries
|
# - template the include entries
|
||||||
# - parse and integrate included elements (see below)
|
|
||||||
# - parse profiles
|
# - parse profiles
|
||||||
# - parse dotfiles
|
# - parse dotfiles
|
||||||
|
# - parse other elements (actions, trans_r, trans_w)
|
||||||
|
# - parse and integrate included elements (see below)
|
||||||
#
|
#
|
||||||
# parse "include" entry
|
# parse "include" entry
|
||||||
# - same as parse config file
|
# - same as parse config file
|
||||||
@@ -124,7 +128,6 @@ class CfgYaml:
|
|||||||
# - document dvars are executed in their own config file
|
# - document dvars are executed in their own config file
|
||||||
# - remove unused functions/methods
|
# - remove unused functions/methods
|
||||||
# - coverage
|
# - coverage
|
||||||
# - remove ori_*
|
|
||||||
#
|
#
|
||||||
|
|
||||||
def __init__(self, path, profile=None, debug=False):
|
def __init__(self, path, profile=None, debug=False):
|
||||||
@@ -142,6 +145,8 @@ class CfgYaml:
|
|||||||
self._dirty = False
|
self._dirty = False
|
||||||
# indicates the config has been updated
|
# indicates the config has been updated
|
||||||
self._dirty_deprecated = False
|
self._dirty_deprecated = False
|
||||||
|
# profile variables
|
||||||
|
self._profilevarskeys = []
|
||||||
|
|
||||||
# init the dictionaries
|
# init the dictionaries
|
||||||
self.settings = {}
|
self.settings = {}
|
||||||
@@ -159,54 +164,83 @@ class CfgYaml:
|
|||||||
raise YamlException(err)
|
raise YamlException(err)
|
||||||
|
|
||||||
self._yaml_dict = self._load_yaml(self._path)
|
self._yaml_dict = self._load_yaml(self._path)
|
||||||
|
# live patch deprecated entries
|
||||||
|
self._fix_deprecated(self._yaml_dict)
|
||||||
|
|
||||||
|
##################################################
|
||||||
|
# parse the config and variables
|
||||||
|
##################################################
|
||||||
|
|
||||||
# parse the "config" block
|
# parse the "config" block
|
||||||
self.settings = self._parse_blk_settings(self._yaml_dict)
|
self.settings = self._parse_blk_settings(self._yaml_dict)
|
||||||
|
|
||||||
# parse the "variables" block
|
# base templater (when no vars/dvars exist)
|
||||||
self.variables = self._parse_blk_variables(self._yaml_dict)
|
self.variables = self._enrich_vars(self.variables, self._profile)
|
||||||
self._redefine_templater()
|
self._redefine_templater()
|
||||||
|
|
||||||
|
# parse the "variables" block
|
||||||
|
var = self._parse_blk_variables(self._yaml_dict)
|
||||||
|
self._add_variables(var, template=False)
|
||||||
|
|
||||||
# parse the "dynvariables" block
|
# parse the "dynvariables" block
|
||||||
dvariables = self._parse_blk_dynvariables(self._yaml_dict)
|
dvariables = self._parse_blk_dynvariables(self._yaml_dict)
|
||||||
self._add_variables(dvariables)
|
self._add_variables(dvariables, shell=True)
|
||||||
|
|
||||||
# interprete dynvariables
|
# parse the "profiles" block
|
||||||
dvariables = self._template_dict(dvariables)
|
self.profiles = self._parse_blk_profiles(self._yaml_dict)
|
||||||
dvariables = self._shell_exec_dvars(dvariables)
|
|
||||||
|
|
||||||
# merge variables and dynvariables
|
# include the profile's variables/dynvariables
|
||||||
self.variables = self._merge_dict(dvariables, self.variables)
|
# last as it overwrites existing ones
|
||||||
self._redefine_templater()
|
pv, pvd = self._get_profile_included_vars()
|
||||||
|
self._add_variables(pv, prio=True)
|
||||||
|
self._add_variables(pvd, shell=True, prio=True)
|
||||||
|
self._profilevarskeys.extend(pv.keys())
|
||||||
|
self._profilevarskeys.extend(pvd.keys())
|
||||||
|
# TODO handle prio when importing included profile from somewhere else
|
||||||
|
|
||||||
# TODO template variables
|
# template variables
|
||||||
self.variables = self._template_dict(self.variables)
|
self.variables = self._template_dict(self.variables)
|
||||||
if self._debug:
|
if self._debug:
|
||||||
self._debug_dict('variables', self.variables)
|
self._debug_dict('variables', self.variables)
|
||||||
|
|
||||||
|
##################################################
|
||||||
# template the "include" entries
|
# template the "include" entries
|
||||||
|
##################################################
|
||||||
|
|
||||||
self._template_include_entry()
|
self._template_include_entry()
|
||||||
|
|
||||||
|
##################################################
|
||||||
|
# parse the other blocks
|
||||||
|
##################################################
|
||||||
|
|
||||||
|
# parse the "dotfiles" block
|
||||||
|
self.dotfiles = self._parse_blk_dotfiles(self._yaml_dict)
|
||||||
|
# parse the "actions" block
|
||||||
|
self.actions = self._parse_blk_actions(self._yaml_dict)
|
||||||
|
# parse the "trans_r" block
|
||||||
|
self.trans_r = self._parse_blk_trans_r(self._yaml_dict)
|
||||||
|
# parse the "trans_w" block
|
||||||
|
self.trans_w = self._parse_blk_trans_w(self._yaml_dict)
|
||||||
|
|
||||||
|
##################################################
|
||||||
|
# import elements
|
||||||
|
##################################################
|
||||||
|
|
||||||
# process imported variables (import_variables)
|
# process imported variables (import_variables)
|
||||||
newvars = self._import_variables()
|
newvars = self._import_variables()
|
||||||
|
self._clear_profile_vars(newvars)
|
||||||
self._add_variables(newvars)
|
self._add_variables(newvars)
|
||||||
|
|
||||||
# TODO process imported actions (import_actions)
|
# process imported actions (import_actions)
|
||||||
self._import_actions()
|
self._import_actions()
|
||||||
# TODO process imported profile dotfiles (import)
|
# process imported profile dotfiles (import)
|
||||||
self._import_profiles_dotfiles()
|
self._import_profiles_dotfiles()
|
||||||
# TODO process imported configs (import_configs)
|
# process imported configs (import_configs)
|
||||||
self._import_configs()
|
self._import_configs()
|
||||||
|
|
||||||
# process profile include
|
# process profile include
|
||||||
self._resolve_profile_includes()
|
self._resolve_profile_includes()
|
||||||
|
|
||||||
# =====
|
|
||||||
# TODO below
|
|
||||||
# ====
|
|
||||||
if self._debug:
|
|
||||||
self._log.dbg('BEFORE normalization: {}'.format(self._yaml_dict))
|
|
||||||
|
|
||||||
# resolve variables
|
# resolve variables
|
||||||
# TODO
|
# TODO
|
||||||
# self.variables, self.prokeys = self._merge_variables()
|
# self.variables, self.prokeys = self._merge_variables()
|
||||||
@@ -217,32 +251,43 @@ class CfgYaml:
|
|||||||
# process profile ALL
|
# process profile ALL
|
||||||
self._resolve_profile_all()
|
self._resolve_profile_all()
|
||||||
# patch dotfiles paths
|
# patch dotfiles paths
|
||||||
self._resolve_dotfile_paths()
|
self._template_dotfiles_paths()
|
||||||
|
|
||||||
# TODO ensure no element is left un-templated at the end
|
# TODO ensure no element is left un-templated at the end
|
||||||
|
|
||||||
if self._debug:
|
if self._debug:
|
||||||
self._log.dbg('AFTER normalization: {}'.format(self._yaml_dict))
|
self._log.dbg('########### {} ###########'.format('final config'))
|
||||||
|
self._debug_entries()
|
||||||
|
|
||||||
def _add_variables(self, ext_variables):
|
def _add_variables(self, new, shell=False, template=True, prio=False):
|
||||||
"""add new variables from external file"""
|
"""
|
||||||
|
add new variables
|
||||||
|
@shell: execute the variable through the shell
|
||||||
|
@template: template the variable
|
||||||
|
@prio: new takes priority over existing variables
|
||||||
|
"""
|
||||||
# TODO move me
|
# TODO move me
|
||||||
|
if not new:
|
||||||
|
return
|
||||||
# merge
|
# merge
|
||||||
self.variables = self._merge_dict(self.variables, ext_variables)
|
if prio:
|
||||||
|
self.variables = self._merge_dict(new, self.variables)
|
||||||
|
else:
|
||||||
|
self.variables = self._merge_dict(self.variables, new)
|
||||||
# ensure enriched variables are relative to this config
|
# ensure enriched variables are relative to this config
|
||||||
self.variables = self._enrich_vars(self.variables, self._profile)
|
self.variables = self._enrich_vars(self.variables, self._profile)
|
||||||
# re-create the templater
|
# re-create the templater
|
||||||
self._redefine_templater()
|
self._redefine_templater()
|
||||||
# rec resolve variables with new ones
|
if template:
|
||||||
self.variables = self._template_dict(self.variables)
|
# rec resolve variables with new ones
|
||||||
|
self._rec_resolve_variables(self.variables)
|
||||||
def _redefine_templater(self):
|
if self._debug:
|
||||||
"""create templater based on current variables"""
|
self._debug_dict('variables', self.variables)
|
||||||
fufile = self.settings[Settings.key_func_file]
|
if shell:
|
||||||
fifile = self.settings[Settings.key_filter_file]
|
# shell exec
|
||||||
self._tmpl = Templategen(variables=self.variables,
|
self._shell_exec_dvars(self.variables, keys=new.keys())
|
||||||
func_file=fufile,
|
# re-create the templater
|
||||||
filter_file=fifile)
|
self._redefine_templater()
|
||||||
|
|
||||||
########################################################
|
########################################################
|
||||||
# outside available methods
|
# outside available methods
|
||||||
@@ -307,6 +352,8 @@ class CfgYaml:
|
|||||||
return False
|
return False
|
||||||
if self._debug:
|
if self._debug:
|
||||||
self._log.dbg('adding new dotfile: {}'.format(key))
|
self._log.dbg('adding new dotfile: {}'.format(key))
|
||||||
|
self._log.dbg('new dotfile src: {}'.format(src))
|
||||||
|
self._log.dbg('new dotfile dst: {}'.format(dst))
|
||||||
|
|
||||||
df_dict = {
|
df_dict = {
|
||||||
self.key_dotfile_src: src,
|
self.key_dotfile_src: src,
|
||||||
@@ -430,33 +477,33 @@ class CfgYaml:
|
|||||||
|
|
||||||
def _parse_blk_dotfiles(self, dic):
|
def _parse_blk_dotfiles(self, dic):
|
||||||
"""parse the "dotfiles" block"""
|
"""parse the "dotfiles" block"""
|
||||||
self.ori_dotfiles = self._get_entry(dic, self.key_dotfiles)
|
dotfiles = self._get_entry(dic, self.key_dotfiles)
|
||||||
self.dotfiles = deepcopy(self.ori_dotfiles)
|
keys = dotfiles.keys()
|
||||||
keys = self.dotfiles.keys()
|
|
||||||
if len(keys) != len(list(set(keys))):
|
if len(keys) != len(list(set(keys))):
|
||||||
dups = [x for x in keys if x not in list(set(keys))]
|
dups = [x for x in keys if x not in list(set(keys))]
|
||||||
err = 'duplicate dotfile keys found: {}'.format(dups)
|
err = 'duplicate dotfile keys found: {}'.format(dups)
|
||||||
raise YamlException(err)
|
raise YamlException(err)
|
||||||
self.dotfiles = self._norm_dotfiles(self.dotfiles)
|
dotfiles = self._norm_dotfiles(dotfiles)
|
||||||
if self._debug:
|
if self._debug:
|
||||||
self._debug_dict('dotfiles', self.dotfiles)
|
self._debug_dict('dotfiles', dotfiles)
|
||||||
|
return dotfiles
|
||||||
|
|
||||||
def _parse_blk_profiles(self, dic):
|
def _parse_blk_profiles(self, dic):
|
||||||
"""parse the "profiles" block"""
|
"""parse the "profiles" block"""
|
||||||
self.ori_profiles = self._get_entry(dic, self.key_profiles)
|
profiles = self._get_entry(dic, self.key_profiles)
|
||||||
self.profiles = deepcopy(self.ori_profiles)
|
profiles = self._norm_profiles(profiles)
|
||||||
self.profiles = self._norm_profiles(self.profiles)
|
|
||||||
if self._debug:
|
if self._debug:
|
||||||
self._debug_dict('profiles', self.profiles)
|
self._debug_dict('profiles', profiles)
|
||||||
|
return profiles
|
||||||
|
|
||||||
def _parse_blk_actions(self, dic):
|
def _parse_blk_actions(self, dic):
|
||||||
"""parse the "actions" block"""
|
"""parse the "actions" block"""
|
||||||
self.ori_actions = self._get_entry(dic, self.key_actions,
|
actions = self._get_entry(dic, self.key_actions,
|
||||||
mandatory=False)
|
mandatory=False)
|
||||||
self.actions = deepcopy(self.ori_actions)
|
actions = self._norm_actions(actions)
|
||||||
self.actions = self._norm_actions(self.actions)
|
|
||||||
if self._debug:
|
if self._debug:
|
||||||
self._debug_dict('actions', self.actions)
|
self._debug_dict('actions', actions)
|
||||||
|
return actions
|
||||||
|
|
||||||
def _parse_blk_trans_r(self, dic):
|
def _parse_blk_trans_r(self, dic):
|
||||||
"""parse the "trans_r" block"""
|
"""parse the "trans_r" block"""
|
||||||
@@ -466,18 +513,18 @@ class CfgYaml:
|
|||||||
self._log.warn(msg)
|
self._log.warn(msg)
|
||||||
dic[self.key_trans_r] = dic[self.old_key_trans_r]
|
dic[self.key_trans_r] = dic[self.old_key_trans_r]
|
||||||
del dic[self.old_key_trans_r]
|
del dic[self.old_key_trans_r]
|
||||||
self.ori_trans_r = self._get_entry(dic, key, mandatory=False)
|
trans_r = self._get_entry(dic, key, mandatory=False)
|
||||||
self.trans_r = deepcopy(self.ori_trans_r)
|
|
||||||
if self._debug:
|
if self._debug:
|
||||||
self._debug_dict('trans_r', self.trans_r)
|
self._debug_dict('trans_r', trans_r)
|
||||||
|
return trans_r
|
||||||
|
|
||||||
def _parse_blk_trans_w(self, dic):
|
def _parse_blk_trans_w(self, dic):
|
||||||
"""parse the "trans_w" block"""
|
"""parse the "trans_w" block"""
|
||||||
self.ori_trans_w = self._get_entry(dic, self.key_trans_w,
|
trans_w = self._get_entry(dic, self.key_trans_w,
|
||||||
mandatory=False)
|
mandatory=False)
|
||||||
self.trans_w = deepcopy(self.ori_trans_w)
|
|
||||||
if self._debug:
|
if self._debug:
|
||||||
self._debug_dict('trans_w', self.trans_w)
|
self._debug_dict('trans_w', trans_w)
|
||||||
|
return trans_w
|
||||||
|
|
||||||
def _parse_blk_variables(self, dic):
|
def _parse_blk_variables(self, dic):
|
||||||
"""parse the "variables" block"""
|
"""parse the "variables" block"""
|
||||||
@@ -500,71 +547,13 @@ class CfgYaml:
|
|||||||
########################################################
|
########################################################
|
||||||
# parsing helpers
|
# parsing helpers
|
||||||
########################################################
|
########################################################
|
||||||
|
|
||||||
def _resolve_dotfile_paths(self):
|
|
||||||
"""resolve dotfiles paths"""
|
|
||||||
# TODO remove
|
|
||||||
t = Templategen(variables=self.variables,
|
|
||||||
func_file=self.settings[Settings.key_func_file],
|
|
||||||
filter_file=self.settings[Settings.key_filter_file])
|
|
||||||
|
|
||||||
for dotfile in self.dotfiles.values():
|
|
||||||
# src
|
|
||||||
src = dotfile[self.key_dotfile_src]
|
|
||||||
newsrc = self.resolve_dotfile_src(src, templater=t)
|
|
||||||
dotfile[self.key_dotfile_src] = newsrc
|
|
||||||
# dst
|
|
||||||
dst = dotfile[self.key_dotfile_dst]
|
|
||||||
newdst = self.resolve_dotfile_dst(dst, templater=t)
|
|
||||||
dotfile[self.key_dotfile_dst] = newdst
|
|
||||||
|
|
||||||
def _rec_resolve_vars(self, variables):
|
|
||||||
"""recursive resolve variables"""
|
|
||||||
# TODO remove this and any call
|
|
||||||
default = self._get_variables_dict(self._profile)
|
|
||||||
t = Templategen(variables=self._merge_dict(default, variables),
|
|
||||||
func_file=self.settings[Settings.key_func_file],
|
|
||||||
filter_file=self.settings[Settings.key_filter_file])
|
|
||||||
for k in variables.keys():
|
|
||||||
val = variables[k]
|
|
||||||
while Templategen.var_is_template(val):
|
|
||||||
val = t.generate_string(val)
|
|
||||||
variables[k] = val
|
|
||||||
t.update_variables(variables)
|
|
||||||
return variables
|
|
||||||
|
|
||||||
def _get_profile_included_vars(self, tvars):
|
|
||||||
"""resolve profile included variables/dynvariables"""
|
|
||||||
# TODO remove
|
|
||||||
t = Templategen(variables=tvars,
|
|
||||||
func_file=self.settings[Settings.key_func_file],
|
|
||||||
filter_file=self.settings[Settings.key_filter_file])
|
|
||||||
|
|
||||||
for k, v in self.profiles.items():
|
|
||||||
if self.key_profile_include in v:
|
|
||||||
new = []
|
|
||||||
for x in v[self.key_profile_include]:
|
|
||||||
new.append(t.generate_string(x))
|
|
||||||
v[self.key_profile_include] = new
|
|
||||||
|
|
||||||
# now get the included ones
|
|
||||||
pro_var = self._get_profile_included_item(self._profile,
|
|
||||||
self.key_profile_variables,
|
|
||||||
seen=[self._profile])
|
|
||||||
pro_dvar = self._get_profile_included_item(self._profile,
|
|
||||||
self.key_profile_dvariables,
|
|
||||||
seen=[self._profile])
|
|
||||||
|
|
||||||
# exec incl dynvariables
|
|
||||||
self._shell_exec_dvars(pro_dvar.keys(), pro_dvar)
|
|
||||||
return pro_var, pro_dvar
|
|
||||||
|
|
||||||
def _merge_variables(self):
|
def _merge_variables(self):
|
||||||
"""
|
"""
|
||||||
resolve all variables across the config
|
resolve all variables across the config
|
||||||
apply them to any needed entries
|
apply them to any needed entries
|
||||||
and return the full list of variables
|
and return the full list of variables
|
||||||
"""
|
"""
|
||||||
|
# TODO remove me ???
|
||||||
if self._debug:
|
if self._debug:
|
||||||
self._log.dbg('get local variables')
|
self._log.dbg('get local variables')
|
||||||
|
|
||||||
@@ -587,7 +576,7 @@ class CfgYaml:
|
|||||||
self._debug_dict('variables', merged)
|
self._debug_dict('variables', merged)
|
||||||
|
|
||||||
# resolve profile included variables/dynvariables
|
# resolve profile included variables/dynvariables
|
||||||
pro_var, pro_dvar = self._get_profile_included_vars(merged)
|
pro_var, pro_dvar = self._get_profile_included_vars()
|
||||||
|
|
||||||
# merge all and resolve
|
# merge all and resolve
|
||||||
merged = self._merge_dict(pro_var, merged)
|
merged = self._merge_dict(pro_var, merged)
|
||||||
@@ -706,6 +695,7 @@ class CfgYaml:
|
|||||||
|
|
||||||
def _get_dvariables_dict(self):
|
def _get_dvariables_dict(self):
|
||||||
"""return dynvariables"""
|
"""return dynvariables"""
|
||||||
|
# TODO remove me ??
|
||||||
variables = deepcopy(self.ori_dvariables)
|
variables = deepcopy(self.ori_dvariables)
|
||||||
return variables
|
return variables
|
||||||
|
|
||||||
@@ -746,7 +736,7 @@ class CfgYaml:
|
|||||||
v[self.key_profile_dotfiles] = self.dotfiles.keys()
|
v[self.key_profile_dotfiles] = self.dotfiles.keys()
|
||||||
|
|
||||||
def _resolve_profile_includes(self):
|
def _resolve_profile_includes(self):
|
||||||
# profiles -> include other profile
|
"""resolve profile(s) including other profiles"""
|
||||||
for k, v in self.profiles.items():
|
for k, v in self.profiles.items():
|
||||||
self._rec_resolve_profile_include(k)
|
self._rec_resolve_profile_include(k)
|
||||||
|
|
||||||
@@ -768,6 +758,7 @@ class CfgYaml:
|
|||||||
dotfiles = this_profile.get(self.key_profile_dotfiles, []) or []
|
dotfiles = this_profile.get(self.key_profile_dotfiles, []) or []
|
||||||
actions = this_profile.get(self.key_profile_actions, []) or []
|
actions = this_profile.get(self.key_profile_actions, []) or []
|
||||||
includes = this_profile.get(self.key_profile_include, []) or []
|
includes = this_profile.get(self.key_profile_include, []) or []
|
||||||
|
# TODO ignore those as already included through _get_profiles_vars
|
||||||
pvars = this_profile.get(self.key_profile_variables, {}) or {}
|
pvars = this_profile.get(self.key_profile_variables, {}) or {}
|
||||||
pdvars = this_profile.get(self.key_profile_dvariables, {}) or {}
|
pdvars = this_profile.get(self.key_profile_dvariables, {}) or {}
|
||||||
if not includes:
|
if not includes:
|
||||||
@@ -853,7 +844,7 @@ class CfgYaml:
|
|||||||
if profile == self._profile:
|
if profile == self._profile:
|
||||||
# Only for the selected profile, we execute dynamic variables and
|
# Only for the selected profile, we execute dynamic variables and
|
||||||
# we merge variables/dynvariables into the global variables
|
# we merge variables/dynvariables into the global variables
|
||||||
self._shell_exec_dvars(pdvars.keys(), pdvars)
|
self._shell_exec_dvars(pdvars)
|
||||||
self.variables = self._merge_dict(pvars, self.variables)
|
self.variables = self._merge_dict(pvars, self.variables)
|
||||||
self.variables = self._merge_dict(pdvars, self.variables)
|
self.variables = self._merge_dict(pdvars, self.variables)
|
||||||
|
|
||||||
@@ -882,11 +873,15 @@ class CfgYaml:
|
|||||||
self._log.dbg('import dynvariables from {}'.format(path))
|
self._log.dbg('import dynvariables from {}'.format(path))
|
||||||
dvar = self._import_sub(path, self.key_dvariables,
|
dvar = self._import_sub(path, self.key_dvariables,
|
||||||
mandatory=False)
|
mandatory=False)
|
||||||
|
|
||||||
merged = self._merge_dict(dvar, var)
|
merged = self._merge_dict(dvar, var)
|
||||||
merged = self._template_dict(merged)
|
self._rec_resolve_variables(merged)
|
||||||
|
if dvar.keys():
|
||||||
|
self._shell_exec_dvars(merged, keys=dvar.keys())
|
||||||
|
self._clear_profile_vars(merged)
|
||||||
newvars = self._merge_dict(newvars, merged)
|
newvars = self._merge_dict(newvars, merged)
|
||||||
# TODO needed?
|
if self._debug:
|
||||||
# self._clear_profile_vars(merged)
|
self._debug_dict('imported variables', newvars)
|
||||||
return newvars
|
return newvars
|
||||||
|
|
||||||
def _import_actions(self):
|
def _import_actions(self):
|
||||||
@@ -999,6 +994,8 @@ class CfgYaml:
|
|||||||
|
|
||||||
def _fix_deprecated(self, yamldict):
|
def _fix_deprecated(self, yamldict):
|
||||||
"""fix deprecated entries"""
|
"""fix deprecated entries"""
|
||||||
|
if not yamldict:
|
||||||
|
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
|
return yamldict
|
||||||
@@ -1088,8 +1085,7 @@ class CfgYaml:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self._log.err(e)
|
self._log.err(e)
|
||||||
raise YamlException('invalid config: {}'.format(path))
|
raise YamlException('invalid config: {}'.format(path))
|
||||||
# live patch deprecated entries
|
return content
|
||||||
return self._fix_deprecated(content)
|
|
||||||
|
|
||||||
def _yaml_load(self, path):
|
def _yaml_load(self, path):
|
||||||
"""load from yaml"""
|
"""load from yaml"""
|
||||||
@@ -1107,14 +1103,120 @@ class CfgYaml:
|
|||||||
y.typ = 'rt'
|
y.typ = 'rt'
|
||||||
y.dump(content, where)
|
y.dump(content, where)
|
||||||
|
|
||||||
|
########################################################
|
||||||
|
# templating
|
||||||
|
########################################################
|
||||||
|
|
||||||
|
def _redefine_templater(self):
|
||||||
|
"""create templater based on current variables"""
|
||||||
|
fufile = self.settings[Settings.key_func_file]
|
||||||
|
fifile = self.settings[Settings.key_filter_file]
|
||||||
|
self._tmpl = Templategen(variables=self.variables,
|
||||||
|
func_file=fufile,
|
||||||
|
filter_file=fifile)
|
||||||
|
|
||||||
|
def _template_item(self, item, exc_if_fail=True):
|
||||||
|
"""
|
||||||
|
template an item using the templategen
|
||||||
|
will raise an exception if template failed and exc_if_fail
|
||||||
|
"""
|
||||||
|
# TODO use this across the entire file
|
||||||
|
if not Templategen.var_is_template(item):
|
||||||
|
return item
|
||||||
|
try:
|
||||||
|
val = item
|
||||||
|
while Templategen.var_is_template(val):
|
||||||
|
val = self._tmpl.generate_string(val)
|
||||||
|
except UndefinedException as e:
|
||||||
|
if exc_if_fail:
|
||||||
|
raise e
|
||||||
|
return val
|
||||||
|
|
||||||
|
def _template_list(self, entries):
|
||||||
|
"""template a list of entries"""
|
||||||
|
new = []
|
||||||
|
if not entries:
|
||||||
|
return new
|
||||||
|
for e in entries:
|
||||||
|
et = self._template_item(e)
|
||||||
|
if self._debug and e != et:
|
||||||
|
self._log.dbg('resolved: {} -> {}'.format(e, et))
|
||||||
|
new.append(et)
|
||||||
|
return new
|
||||||
|
|
||||||
|
def _template_dict(self, entries):
|
||||||
|
"""template a dictionary of entries"""
|
||||||
|
new = {}
|
||||||
|
if not entries:
|
||||||
|
return new
|
||||||
|
for k, v in entries.items():
|
||||||
|
vt = self._template_item(v)
|
||||||
|
if self._debug and v != vt:
|
||||||
|
self._log.dbg('resolved: {} -> {}'.format(v, vt))
|
||||||
|
new[k] = vt
|
||||||
|
return new
|
||||||
|
|
||||||
|
def _template_dotfiles_paths(self):
|
||||||
|
"""template dotfiles paths"""
|
||||||
|
for dotfile in self.dotfiles.values():
|
||||||
|
# src
|
||||||
|
src = dotfile[self.key_dotfile_src]
|
||||||
|
newsrc = self.resolve_dotfile_src(src, templater=self._tmpl)
|
||||||
|
dotfile[self.key_dotfile_src] = newsrc
|
||||||
|
# dst
|
||||||
|
dst = dotfile[self.key_dotfile_dst]
|
||||||
|
newdst = self.resolve_dotfile_dst(dst, templater=self._tmpl)
|
||||||
|
dotfile[self.key_dotfile_dst] = newdst
|
||||||
|
|
||||||
|
def _rec_resolve_variables(self, variables):
|
||||||
|
"""recursive resolve variables"""
|
||||||
|
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])
|
||||||
|
for k in variables.keys():
|
||||||
|
val = variables[k]
|
||||||
|
while Templategen.var_is_template(val):
|
||||||
|
val = t.generate_string(val)
|
||||||
|
variables[k] = val
|
||||||
|
t.update_variables(variables)
|
||||||
|
|
||||||
|
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:
|
||||||
|
new = []
|
||||||
|
for x in v[self.key_profile_include]:
|
||||||
|
new.append(self._tmpl.generate_string(x))
|
||||||
|
v[self.key_profile_include] = new
|
||||||
|
|
||||||
|
# now get the included ones
|
||||||
|
pro_var = self._get_profile_included_item(self._profile,
|
||||||
|
self.key_profile_variables,
|
||||||
|
seen=[self._profile])
|
||||||
|
pro_dvar = self._get_profile_included_item(self._profile,
|
||||||
|
self.key_profile_dvariables,
|
||||||
|
seen=[self._profile])
|
||||||
|
|
||||||
|
# exec incl dynvariables
|
||||||
|
return pro_var, pro_dvar
|
||||||
|
|
||||||
########################################################
|
########################################################
|
||||||
# helpers
|
# helpers
|
||||||
########################################################
|
########################################################
|
||||||
|
|
||||||
def _clear_profile_vars(self, dic):
|
def _clear_profile_vars(self, dic):
|
||||||
"""remove profile variables from dic if found"""
|
"""
|
||||||
# TODO check why this is used at all
|
remove profile variables from dic if found inplace
|
||||||
[dic.pop(k, None) for k in self.prokeys]
|
to avoid profile variables being overwriten
|
||||||
|
"""
|
||||||
|
# TODO
|
||||||
|
# [dic.pop(k, None) for k in self.prokeys]
|
||||||
|
if not dic:
|
||||||
|
return
|
||||||
|
[dic.pop(k, None) for k in self._profilevarskeys]
|
||||||
|
|
||||||
def _parse_extended_import_path(self, path_entry):
|
def _parse_extended_import_path(self, path_entry):
|
||||||
"""Parse an import path in a tuple (path, fatal_not_found)."""
|
"""Parse an import path in a tuple (path, fatal_not_found)."""
|
||||||
@@ -1161,8 +1263,7 @@ class CfgYaml:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def _process_path(self, path_entry):
|
def _process_path(self, path_entry):
|
||||||
"""Process a path entry to a normalized form.
|
"""
|
||||||
|
|
||||||
This method processed a path entry. Namely it:
|
This method processed a path entry. Namely it:
|
||||||
- Normalizes the path.
|
- Normalizes the path.
|
||||||
- Expands globs.
|
- Expands globs.
|
||||||
@@ -1170,11 +1271,6 @@ class CfgYaml:
|
|||||||
This method always returns a list containing only absolute paths
|
This method always returns a list containing only absolute paths
|
||||||
existing on the filesystem. If the input is not a glob, the list
|
existing on the filesystem. If the input is not a glob, the list
|
||||||
contains at most one element, otheriwse it could hold more.
|
contains at most one element, otheriwse it could hold more.
|
||||||
|
|
||||||
:param path_entry: A path with an optional attribute.
|
|
||||||
:type path_entry: str
|
|
||||||
:return: A list of normalized existing paths, obtained from the input.
|
|
||||||
:rtype: List of str
|
|
||||||
"""
|
"""
|
||||||
path, fatal_not_found = self._parse_extended_import_path(path_entry)
|
path, fatal_not_found = self._parse_extended_import_path(path_entry)
|
||||||
path = self._norm_path(path)
|
path = self._norm_path(path)
|
||||||
@@ -1190,37 +1286,14 @@ class CfgYaml:
|
|||||||
return [p for p in checked_paths if p]
|
return [p for p in checked_paths if p]
|
||||||
|
|
||||||
def _resolve_paths(self, paths):
|
def _resolve_paths(self, paths):
|
||||||
"""Resolve a list of path to existing paths.
|
"""
|
||||||
|
|
||||||
This function resolves a list of paths. This means normalizing,
|
This function resolves a list of paths. This means normalizing,
|
||||||
expanding globs and checking for existence, taking in account
|
expanding globs and checking for existence, taking in account
|
||||||
fatal_not_found flags.
|
fatal_not_found flags.
|
||||||
|
|
||||||
:param paths: A list of paths. Might contain globs and options.
|
|
||||||
:type paths: List of str
|
|
||||||
:return: A list of processed paths.
|
|
||||||
:rtype: List of str
|
|
||||||
"""
|
"""
|
||||||
processed_paths = (self._process_path(p) for p in paths)
|
processed_paths = (self._process_path(p) for p in paths)
|
||||||
return list(chain.from_iterable(processed_paths))
|
return list(chain.from_iterable(processed_paths))
|
||||||
|
|
||||||
def _template_item(self, item, exc_if_fail=True):
|
|
||||||
"""
|
|
||||||
template an item using the templategen
|
|
||||||
will raise an exception if template failed and exc_if_fail
|
|
||||||
"""
|
|
||||||
# TODO use this across the entire file
|
|
||||||
if not Templategen.var_is_template(item):
|
|
||||||
return item
|
|
||||||
try:
|
|
||||||
val = item
|
|
||||||
while Templategen.var_is_template(val):
|
|
||||||
val = self._tmpl.generate_string(val)
|
|
||||||
except UndefinedException as e:
|
|
||||||
if exc_if_fail:
|
|
||||||
raise e
|
|
||||||
return val
|
|
||||||
|
|
||||||
def _merge_dict(self, high, low):
|
def _merge_dict(self, high, low):
|
||||||
"""merge high and low dict"""
|
"""merge high and low dict"""
|
||||||
if not high:
|
if not high:
|
||||||
@@ -1230,16 +1303,16 @@ class CfgYaml:
|
|||||||
return {**low, **high}
|
return {**low, **high}
|
||||||
|
|
||||||
def _get_entry(self, dic, key, mandatory=True):
|
def _get_entry(self, dic, key, mandatory=True):
|
||||||
"""return entry from yaml dictionary"""
|
"""return copy of entry from yaml dictionary"""
|
||||||
if key not in dic:
|
if key not in dic:
|
||||||
if mandatory:
|
if mandatory:
|
||||||
raise YamlException('invalid config: no {} found'.format(key))
|
raise YamlException('invalid config: no {} found'.format(key))
|
||||||
dic[key] = {}
|
dic[key] = {}
|
||||||
return dic[key]
|
return deepcopy(dic[key])
|
||||||
if mandatory and not dic[key]:
|
if mandatory and not dic[key]:
|
||||||
# ensure is not none
|
# ensure is not none
|
||||||
dic[key] = {}
|
dic[key] = {}
|
||||||
return dic[key]
|
return deepcopy(dic[key])
|
||||||
|
|
||||||
def _clear_none(self, dic):
|
def _clear_none(self, dic):
|
||||||
"""recursively delete all none/empty values in a dictionary."""
|
"""recursively delete all none/empty values in a dictionary."""
|
||||||
@@ -1288,11 +1361,13 @@ class CfgYaml:
|
|||||||
self._log.dbg('normalizing: {} -> {}'.format(path, ret))
|
self._log.dbg('normalizing: {} -> {}'.format(path, ret))
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def _shell_exec_dvars(self, dic):
|
def _shell_exec_dvars(self, dic, keys=[]):
|
||||||
"""shell execute dynvariables"""
|
"""shell execute dynvariables in-place"""
|
||||||
# TODO remove other calls outside initial setup of dvars
|
# TODO remove other calls outside initial setup of dvars
|
||||||
executed = {}
|
if not keys:
|
||||||
for k, v in dic.items():
|
keys = dic.keys()
|
||||||
|
for k in keys:
|
||||||
|
v = dic[k]
|
||||||
ret, out = shell(v, debug=self._debug)
|
ret, out = shell(v, debug=self._debug)
|
||||||
if not ret:
|
if not ret:
|
||||||
err = 'var \"{}: {}\" failed: {}'.format(k, v, out)
|
err = 'var \"{}: {}\" failed: {}'.format(k, v, out)
|
||||||
@@ -1300,32 +1375,7 @@ class CfgYaml:
|
|||||||
raise YamlException(err)
|
raise YamlException(err)
|
||||||
if self._debug:
|
if self._debug:
|
||||||
self._log.dbg('\"{}\": {} -> {}'.format(k, v, out))
|
self._log.dbg('\"{}\": {} -> {}'.format(k, v, out))
|
||||||
executed[k] = out
|
dic[k] = out
|
||||||
return executed
|
|
||||||
|
|
||||||
def _template_list(self, entries):
|
|
||||||
"""template a list of entries"""
|
|
||||||
new = []
|
|
||||||
if not entries:
|
|
||||||
return new
|
|
||||||
for e in entries:
|
|
||||||
et = self._template_item(e)
|
|
||||||
if self._debug and e != et:
|
|
||||||
self._log.dbg('resolved: {} -> {}'.format(e, et))
|
|
||||||
new.append(et)
|
|
||||||
return new
|
|
||||||
|
|
||||||
def _template_dict(self, entries):
|
|
||||||
"""template a dictionary of entries"""
|
|
||||||
new = {}
|
|
||||||
if not entries:
|
|
||||||
return new
|
|
||||||
for k, v in entries.items():
|
|
||||||
vt = self._template_item(v)
|
|
||||||
if self._debug and v != vt:
|
|
||||||
self._log.dbg('resolved: {} -> {}'.format(v, vt))
|
|
||||||
new[k] = vt
|
|
||||||
return new
|
|
||||||
|
|
||||||
def _check_minversion(self, minversion):
|
def _check_minversion(self, minversion):
|
||||||
if not minversion:
|
if not minversion:
|
||||||
@@ -1341,6 +1391,18 @@ class CfgYaml:
|
|||||||
err += ' Please update.'
|
err += ' Please update.'
|
||||||
raise YamlException(err)
|
raise YamlException(err)
|
||||||
|
|
||||||
|
def _debug_entries(self):
|
||||||
|
"""debug print all interesting entries"""
|
||||||
|
if not self._debug:
|
||||||
|
return
|
||||||
|
self._debug_dict('settings', self.settings)
|
||||||
|
self._debug_dict('dotfiles', self.dotfiles)
|
||||||
|
self._debug_dict('profiles', self.profiles)
|
||||||
|
self._debug_dict('actions', self.actions)
|
||||||
|
self._debug_dict('trans_r', self.trans_r)
|
||||||
|
self._debug_dict('trans_w', self.trans_w)
|
||||||
|
self._debug_dict('variables', self.variables)
|
||||||
|
|
||||||
def _debug_dict(self, title, elems):
|
def _debug_dict(self, title, elems):
|
||||||
"""pretty print dict"""
|
"""pretty print dict"""
|
||||||
if not self._debug:
|
if not self._debug:
|
||||||
|
|||||||
Reference in New Issue
Block a user