def _parse(self): """parse config file""" # parse all actions if self.key_actions in self.content: if self.content[self.key_actions] is not None: for k, v in self.content[self.key_actions].items(): # loop through all actions if k in [self.key_actions_pre, self.key_actions_post]: # parse pre/post actions items = self.content[self.key_actions][k].items() for k2, v2 in items: if k not in self.actions: self.actions[k] = {} self.actions[k][k2] = Action(k2, k, v2) else: # parse naked actions as post actions if self.key_actions_post not in self.actions: self.actions[self.key_actions_post] = {} self.actions[self.key_actions_post][k] = Action( k, '', v) # parse read transformations if self.key_trans_r in self.content: if self.content[self.key_trans_r] is not None: for k, v in self.content[self.key_trans_r].items(): self.trans_r[k] = Transform(k, v) # parse write transformations if self.key_trans_w in self.content: if self.content[self.key_trans_w] is not None: for k, v in self.content[self.key_trans_w].items(): self.trans_w[k] = Transform(k, v) # parse the profiles self.lnk_profiles = self.content[self.key_profiles] if self.lnk_profiles is None: # ensures self.lnk_profiles is a dict self.content[self.key_profiles] = {} self.lnk_profiles = self.content[self.key_profiles] for k, v in self.lnk_profiles.items(): if self.key_profiles_dots in v and \ v[self.key_profiles_dots] is None: # if has the dotfiles entry but is empty # ensures it's an empty list v[self.key_profiles_dots] = [] # parse the settings self.lnk_settings = self.content[self.key_settings] self._complete_settings() # parse the dotfiles # and construct the dict of objects per dotfile key if not self.content[self.key_dotfiles]: # ensures the dotfiles entry is a dict self.content[self.key_dotfiles] = {} for k, v in self.content[self.key_dotfiles].items(): src = os.path.normpath(v[self.key_dotfiles_src]) dst = os.path.normpath(v[self.key_dotfiles_dst]) # Fail if both `link` and `link_children` present if self.key_dotfiles_link in v \ and self.key_dotfiles_link_children in v: msg = 'only one of `link` or `link_children` allowed per' msg += ' dotfile, error on dotfile "{}".' self.log.err(msg.format(k)) # Otherwise, get link type link = LinkTypes.NOLINK if self.key_dotfiles_link in v and v[self.key_dotfiles_link]: link = LinkTypes.PARENTS if self.key_dotfiles_link_children in v \ and v[self.key_dotfiles_link_children]: link = LinkTypes.CHILDREN noempty = v[self.key_dotfiles_noempty] if \ self.key_dotfiles_noempty \ in v else self.lnk_settings[self.key_ignoreempty] itsactions = v[self.key_dotfiles_actions] if \ self.key_dotfiles_actions in v else [] actions = self._parse_actions(itsactions) # parse read transformation itstrans_r = v[self.key_dotfiles_trans_r] if \ self.key_dotfiles_trans_r in v else None trans_r = None if itstrans_r: if type(itstrans_r) is list: msg = 'One transformation allowed per dotfile' msg += ', error on dotfile \"{}\"' self.log.err(msg.format(k)) msg = 'Please modify your config file to: \"trans: {}\"' self.log.err(msg.format(itstrans_r[0])) msg = 'see https://github.com/deadc0de6/dotdrop/wiki/transformations#config-error-with-transformation-list' # noqa self.log.err(msg) return False trans_r = self._parse_trans(itstrans_r, read=True) if not trans_r: msg = 'unknown trans \"{}\" for \"{}\"' self.log.err(msg.format(itstrans_r, k)) return False # parse write transformation itstrans_w = v[self.key_dotfiles_trans_w] if \ self.key_dotfiles_trans_w in v else None trans_w = None if itstrans_w: if type(itstrans_w) is list: msg = 'One write transformation allowed per dotfile' msg += ', error on dotfile \"{}\"' self.log.err(msg.format(k)) msg = 'Please modify your config file: \"trans_write: {}\"' self.log.err(msg.format(itstrans_w[0])) msg = 'see https://github.com/deadc0de6/dotdrop/wiki/transformations#config-error-with-transformation-list' # noqa self.log.err(msg) return False trans_w = self._parse_trans(itstrans_w, read=False) if not trans_w: msg = 'unknown trans_write \"{}\" for \"{}\"' self.log.err(msg.format(itstrans_w, k)) return False # disable transformation when link is true if link != LinkTypes.NOLINK and (trans_r or trans_w): msg = 'transformations disabled for \"{}\"'.format(dst) msg += ' because link is True' self.log.warn(msg) trans_r = None trans_w = None # parse cmpignore pattern cmpignores = v[self.key_dotfiles_cmpignore] if \ self.key_dotfiles_cmpignore in v else [] # parse upignore pattern upignores = v[self.key_dotfiles_upignore] if \ self.key_dotfiles_upignore in v else [] # create new dotfile self.dotfiles[k] = Dotfile(k, dst, src, link=link, actions=actions, trans_r=trans_r, trans_w=trans_w, cmpignore=cmpignores, noempty=noempty, upignore=upignores) # assign dotfiles to each profile for k, v in self.lnk_profiles.items(): self.prodots[k] = [] if self.key_profiles_dots not in v: # ensures is a list v[self.key_profiles_dots] = [] if not v[self.key_profiles_dots]: continue dots = v[self.key_profiles_dots] if self.key_all in dots: # add all if key ALL is used self.prodots[k] = list(self.dotfiles.values()) else: # add the dotfiles for d in dots: if d not in self.dotfiles: msg = 'unknown dotfile \"{}\" for {}'.format(d, k) self.log.err(msg) continue self.prodots[k].append(self.dotfiles[d]) # handle "include" for each profile for k in self.lnk_profiles.keys(): dots = self._get_included_dotfiles(k) self.prodots[k].extend(dots) # remove duplicates if any self.prodots[k] = list(set(self.prodots[k])) # make sure we have an absolute dotpath self.curdotpath = self.lnk_settings[self.key_dotpath] self.lnk_settings[self.key_dotpath] = \ self._abs_path(self.curdotpath) # make sure we have an absolute workdir self.curworkdir = self.lnk_settings[self.key_workdir] self.lnk_settings[self.key_workdir] = \ self._abs_path(self.curworkdir) return True
def _parse(self): """parse config file""" # parse all actions if self.key_actions in self.content: if self.content[self.key_actions] is not None: for k, v in self.content[self.key_actions].items(): # loop through all actions if k in [self.key_actions_pre, self.key_actions_post]: # parse pre/post actions items = self.content[self.key_actions][k].items() for k2, v2 in items: if k not in self.actions: self.actions[k] = {} self.actions[k][k2] = Action(k2, v2) else: # parse naked actions as post actions if self.key_actions_post not in self.actions: self.actions[self.key_actions_post] = {} self.actions[self.key_actions_post][k] = Action(k, v) # parse all transformations if self.key_trans in self.content: if self.content[self.key_trans] is not None: for k, v in self.content[self.key_trans].items(): self.trans[k] = Transform(k, v) # parse the profiles self.lnk_profiles = self.content[self.key_profiles] if self.lnk_profiles is None: # ensures self.lnk_profiles is a dict self.content[self.key_profiles] = {} self.lnk_profiles = self.content[self.key_profiles] for k, v in self.lnk_profiles.items(): if self.key_profiles_dots in v and \ v[self.key_profiles_dots] is None: # if has the dotfiles entry but is empty # ensures it's an empty list v[self.key_profiles_dots] = [] # parse the settings self.lnk_settings = self.content[self.key_settings] self._complete_settings() # parse the dotfiles # and construct the dict of objects per dotfile key if not self.content[self.key_dotfiles]: # ensures the dotfiles entry is a dict self.content[self.key_dotfiles] = {} for k, v in self.content[self.key_dotfiles].items(): src = v[self.key_dotfiles_src] dst = v[self.key_dotfiles_dst] link = v[self.key_dotfiles_link] if self.key_dotfiles_link \ in v else self.default_link itsactions = v[self.key_dotfiles_actions] if \ self.key_dotfiles_actions in v else [] actions = self._parse_actions(itsactions) itstrans = v[self.key_dotfiles_trans] if \ self.key_dotfiles_trans in v else [] trans = self._parse_trans(itstrans) if len(trans) > 0 and link: msg = 'transformations disabled for \"{}\"'.format(dst) msg += ' because link is True' self.log.warn(msg) trans = [] self.dotfiles[k] = Dotfile(k, dst, src, link=link, actions=actions, trans=trans) # assign dotfiles to each profile for k, v in self.lnk_profiles.items(): self.prodots[k] = [] if self.key_profiles_dots not in v: # ensures is a list v[self.key_profiles_dots] = [] if not v[self.key_profiles_dots]: continue dots = v[self.key_profiles_dots] if self.key_all in dots: # add all if key ALL is used self.prodots[k] = list(self.dotfiles.values()) else: # add the dotfiles self.prodots[k].extend([self.dotfiles[d] for d in dots]) # handle "include" for each profile for k in self.lnk_profiles.keys(): dots = self._get_included_dotfiles(k) self.prodots[k].extend(dots) # remove duplicates if any self.prodots[k] = list(set(self.prodots[k])) # make sure we have an absolute dotpath self.curdotpath = self.lnk_settings[self.key_dotpath] self.lnk_settings[self.key_dotpath] = self.abs_dotpath(self.curdotpath) return True
def _load(self): """load lower level config""" self.cfgyaml = CfgYaml(self.path, self.profile_key, debug=self.debug) # settings self.settings = Settings.parse(None, self.cfgyaml.settings) # dotfiles self.dotfiles = Dotfile.parse_dict(self.cfgyaml.dotfiles) if self.debug: self._debug_list('dotfiles', self.dotfiles) # profiles self.profiles = Profile.parse_dict(self.cfgyaml.profiles) if self.debug: self._debug_list('profiles', self.profiles) # actions self.actions = Action.parse_dict(self.cfgyaml.actions) if self.debug: self._debug_list('actions', self.actions) # trans_r self.trans_r = Transform.parse_dict(self.cfgyaml.trans_r) if self.debug: self._debug_list('trans_r', self.trans_r) # trans_w self.trans_w = Transform.parse_dict(self.cfgyaml.trans_w) if self.debug: self._debug_list('trans_w', self.trans_w) # variables self.variables = self.cfgyaml.variables if self.debug: self._debug_dict('variables', self.variables) # patch dotfiles in profiles self._patch_keys_to_objs(self.profiles, "dotfiles", self.get_dotfile) # patch action in dotfiles actions self._patch_keys_to_objs(self.dotfiles, "actions", self._get_action_w_args) # patch action in profiles actions self._patch_keys_to_objs(self.profiles, "actions", self._get_action_w_args) # patch actions in settings default_actions self._patch_keys_to_objs([self.settings], "default_actions", self._get_action_w_args) if self.debug: msg = 'default actions: {}'.format(self.settings.default_actions) self.log.dbg(msg) # patch trans_w/trans_r in dotfiles self._patch_keys_to_objs(self.dotfiles, "trans_r", self._get_trans_w_args(self._get_trans_r), islist=False) self._patch_keys_to_objs(self.dotfiles, "trans_w", self._get_trans_w_args(self._get_trans_w), islist=False)
def test_update(self): """Test the update function""" # setup some directories fold_config = os.path.join(os.path.expanduser('~'), '.config') create_dir(fold_config) fold_subcfg = os.path.join(os.path.expanduser('~'), '.config', get_string(5)) create_dir(fold_subcfg) self.addCleanup(clean, fold_subcfg) fold_tmp = get_tempdir() create_dir(fold_tmp) self.addCleanup(clean, fold_tmp) # create the directories tmp = get_tempdir() self.assertTrue(os.path.exists(tmp)) self.addCleanup(clean, tmp) dotfilespath = get_tempdir() self.assertTrue(os.path.exists(dotfilespath)) self.addCleanup(clean, dotfilespath) # create the dotfiles to test d1, c1 = create_random_file(fold_config) self.assertTrue(os.path.exists(d1)) self.addCleanup(clean, d1) d2, c2 = create_random_file(fold_config) self.assertTrue(os.path.exists(d2)) self.addCleanup(clean, d2) # template d3t, c3t = create_random_file(fold_config) self.assertTrue(os.path.exists(d3t)) self.addCleanup(clean, d3t) # sub dirs dsubstmp = get_tempdir() self.assertTrue(os.path.exists(dsubstmp)) self.addCleanup(clean, dsubstmp) dirsubs = os.path.basename(dsubstmp) dir1string = 'somedir' dir1 = os.path.join(dsubstmp, dir1string) create_dir(dir1) dir1sub1str = 'sub1' sub1 = os.path.join(dir1, dir1sub1str) create_dir(sub1) dir1sub2str = 'sub2' sub2 = os.path.join(dir1, dir1sub2str) create_dir(sub2) f1s1, f1s1c1 = create_random_file(sub1) self.assertTrue(os.path.exists(f1s1)) f1s2, f1s2c1 = create_random_file(sub2) self.assertTrue(os.path.exists(f1s2)) # create the directory to test dpath = os.path.join(fold_config, get_string(5)) dir1 = create_dir(dpath) dirf1, _ = create_random_file(dpath) self.addCleanup(clean, dir1) # create the config file profile = get_string(5) confpath = create_fake_config(dotfilespath, configname=self.CONFIG_NAME, dotpath=self.CONFIG_DOTPATH, backup=self.CONFIG_BACKUP, create=self.CONFIG_CREATE) self.assertTrue(os.path.exists(confpath)) o = load_options(confpath, profile) o.debug = True o.update_showpatch = True dfiles = [d1, dir1, d2, d3t, dsubstmp] # import the files o.import_path = dfiles cmd_importer(o) # get new config o = load_options(confpath, profile) o.safe = False o.debug = True o.update_showpatch = True trans = Transform('trans', 'cp -r {0} {1}') d3tb = os.path.basename(d3t) for dotfile in o.dotfiles: if os.path.basename(dotfile.dst) == d3tb: # patch the template src = os.path.join(o.dotpath, dotfile.src) src = os.path.expanduser(src) edit_content(src, '{{@@ profile @@}}') if os.path.basename(dotfile.dst) == dirsubs: # retrieve the path of the sub in the dotpath d1indotpath = os.path.join(o.dotpath, dotfile.src) d1indotpath = os.path.expanduser(d1indotpath) dotfile.trans_w = trans # update template o.update_path = [d3t] self.assertFalse(cmd_update(o)) # update sub dirs gone = os.path.join(d1indotpath, dir1string) gone = os.path.join(gone, dir1sub1str) self.assertTrue(os.path.exists(gone)) clean(sub1) # dir1sub1str self.assertTrue(os.path.exists(gone)) o.update_path = [dsubstmp] cmd_update(o) self.assertFalse(os.path.exists(gone)) # edit the files edit_content(d1, 'newcontent') edit_content(dirf1, 'newcontent') # add more file dirf2, _ = create_random_file(dpath) # add more dirs dpath = os.path.join(dpath, get_string(5)) create_dir(dpath) create_random_file(dpath) # update it o.update_path = [d1, dir1] cmd_update(o) # test content newcontent = open(d1, 'r').read() self.assertTrue(newcontent == 'newcontent') newcontent = open(dirf1, 'r').read() self.assertTrue(newcontent == 'newcontent') edit_content(d2, 'newcontentbykey') # update it by key dfiles = o.dotfiles d2key = '' for ds in dfiles: t = os.path.expanduser(ds.dst) if t == d2: d2key = ds.key break self.assertTrue(d2key != '') o.update_path = [d2key] o.update_iskey = True cmd_update(o) # test content newcontent = open(d2, 'r').read() self.assertTrue(newcontent == 'newcontentbykey')