Source code for inicheck.changes

from . iniparse import parse_sections, parse_items, parse_changes
from . entries import ConfigEntry
from . utilities import mk_lst, parse_date
import importlib
from os.path import join as pjoin
from os.path import abspath
from collections import OrderedDict


[docs]class ChangeLog(object): def __init__(self, paths=None, mcfg=None, **kwargs): # Paths to changelogs self.paths = [] # We got a direct path if paths != None: paths = mk_lst(paths) self.paths += paths self.changes = self.join_changes(self.paths) self.check_log_validity(mcfg)
[docs] def join_changes(self, paths): """ Joins multiple changelogs together Args: paths: List of paths containing changelogs syntax Returns: changes: lists of line item changes provided in each line as a list of [old, new] """ changes = [] for p in paths: changes += self.read_change_log(p) return changes
[docs] def read_change_log(self, path): """ Opens the file and reads in sections and the items. Args: path: Path to a changlog file """ with open(path, encoding='utf-8') as f: lines = f.readlines() f.close() sections = parse_sections(lines) raw_changes = sections['changes'].copy() del sections['changes'] # Parse meta data the same way as everything sec_and_items = parse_items(sections) self.info = sec_and_items['meta']['info'] self.date = parse_date(sec_and_items["meta"]['date']) # Parse the change list changes = parse_changes(raw_changes) return changes
[docs] def check_log_validity(self, mcfg): """ Checks the current master config that the items we're moving to are valid. Also confirms that removals are aligned with the master. """ action_kw = ["any",'removed'] cfe = ConfigEntry() invalids = [] for zz,c in enumerate(self.changes): # Check the new assignments match the names of current master items current = c[1] valids = [] # KW removed or ANY is valid for sections if current[0] in action_kw or current[0] in mcfg.cfg.keys(): valids.append(True) # Section is valid, lets check items if current[0] != "removed": if len(valids) >= 1: # Non-any item and any section provided if current[0] == "any" and current[1] not in action_kw: for s, items in mcfg.cfg.items(): if current[1] in items.keys(): valids.append(True) break # Valid item check whe valid section provided elif (current[1] in action_kw or current[1] in mcfg.cfg[current[0]].keys()): valids.append(True) # Check for valid properties if len(valids) >= 2: if current[2] in ["any", "default"]: valids.append(True) # TODO Actually check values, ignoring for now. if len(valids) == 3: valids.append(True) # Final check if len(valids) != len(current): invalids.append(c) # Form a coherent message about incorrect changlog stuff if invalids: msg = ("Changelog states a change that doesn't match the core config." " For a change to be valid the new changes must be in the" " Master Config file. Mismatches are:") for n in invalids: msg+="\n * " msg+="/".join(n[0]) msg+=" -> " msg+="/".join(n[1]) raise ValueError(msg)
[docs] def get_active_changes(self, ucfg): """ Goes through the users config looking for matching old configurations, then makes recommendations. Args: ucfg: UserConfig Object """ required_changes = [] potential_changes = [] cfe = ConfigEntry() cfg = ucfg.cfg for change in self.changes: # Go through the sections for s in cfg.keys(): # Go through the items for i in cfg[s].keys(): # Assign original changes to any's assumed = change[0].copy() new = change[1].copy() # Swap out anys with section names if change[0][0] == "any": assumed[0] = s if change[1][0] != "removed": new[0] = s # swap out anys with item names if change[0][1] == "any": assumed[1] = i if change[1][0] != "removed": new[1] = i # If we have a match from the changelog and the ucfg if assumed[0] == s and assumed[1] == i: if "removed" in new: required_changes.append([assumed, "removed"]) # Make sure "any" doesn't disagree with master elif new[0] in ucfg.mcfg.cfg.keys(): if new[1] in ucfg.mcfg.cfg[new[0]].keys(): # Check for an old default match and suggest a change if assumed[2] == "default": value = str(mk_lst(cfg[s][i], unlst=True)) if value == assumed[3]: potential_changes.append([assumed, new]) else: required_changes.append([assumed, new]) return potential_changes, required_changes
[docs] def apply_changes(self, ucfg, potentials, changes): """ Iterate through the list of detectd applicable changes retrieved from get_actived_changes to produce a new config file with the correct changes. Args: ucfg: UserConifg Object changes: list lists length 4 (section item property value) representing detected changes Returns: cfg: Config dictionary """ cfg = ucfg.cfg.copy() # REVIEW Right now only defaults can be changed so we assume that here for p in potentials: s_o = p[0][0] i_o = p[0][1] # assign the new defaults cfg[s_o][i_o] = p[1][3] for c in changes: removal = False s_o = c[0][0] i_o = c[0][1] # Avoid adding removed items if c[1] == "removed": removal = True else: s_n = c[1][0] i_n = c[1][1] #print(s_o, i_o, s_n, i_n) # Confirm new section in the config if not removal: if s_n not in ucfg.cfg.keys(): cfg[s_n] = OrderedDict() # Its been deprecated if removal: del cfg[s_o][i_o] # Item or section is a name transfer. elif s_o in ucfg.cfg.keys(): if i_o in cfg[s_o].keys(): if s_n != "removed": cfg[s_n][i_n] = cfg[s_o][i_o] del cfg[s_o][i_o] # look to remove a whole section if len(cfg[s_o].keys()) == 0 and s_o not in ucfg.mcfg.cfg.keys(): del(cfg[s_o]) return cfg