Source code for drymass.cli.config

import pathlib

from . import definitions
from . import parse_funcs
from .._version import version

#: DryMass configuration file name
FILE_CONFIG = "drymass.cfg"


[docs]class ConfigFile(object): def __init__(self, path): """DryMass configuration file management Manage configuration file of an experimental data set with restrictions imposed by :data:`drymass.cli.definitions.config`. Parameters ---------- path: str path to the configuration file """ path = pathlib.Path(path).resolve() if path.is_dir(): path = path / FILE_CONFIG path.touch() self.path = path def __getitem__(self, section): """Get a configuration section Parameters ---------- section: str the configuration section Returns ------- sectiondict: dict the configuration section dictionary """ datadict = self._parse() if section in datadict: return datadict[section] elif section in definitions.config: # return default values secd = {} for kk in definitions.config[section]: secd[kk] = definitions.config[section][kk][0] # write result datadict[section] = secd self._write(datadict) return secd else: raise ValueError("Unknown section title: {}".format(section)) def __setitem__(self, section, sectiondict): """Replace a section in the configuration file Parameters ---------- section: str the section name sectiondict: dict the configuration dictionary Notes ----- The previous content of the configuration section in the configuration file is replaced. """ datadict = self._parse() for key in sectiondict: self._check_value(section, key, sectiondict[key]) datadict[section] = sectiondict self._write(datadict) def _check_value(self, section, key, value): """Check if a section/key/value pair is valid Raises `ValueError` if this is not the case. Returns `None`. """ if section not in definitions.config: raise ValueError("Unknown section title: {}".format(section)) if key not in definitions.config[section]: raise ValueError("Unknown key: {}: {}".format(section, key)) if value in [None, "none", "None", "()", "[]"]: if definitions.config[section][key][0] is not None: msg = "Value 'None' not allowed for [{}]: {}!".format( section, key) raise ValueError(msg) ret_value = None else: type_func = definitions.config[section][key][1] try: type_func(value) except BaseException as e: msg = "Failed to parse: '[{}]: {}={}'".format(section, key, value) e.args = ("{}; {}".format(msg, ", ".join(e.args)),) raise ret_value = type_func(value) return ret_value def _parse_compat(self, section, key, value): if section == "bg": # drymass < 0.1.3: API changed in qpimage 0.1.6 if (key in ["amplitude profile", "phase profile"] and value == "ramp"): value = "tilt" elif section == "roi": # drymass <= 0.1.5: keys were renamed to reflect pixel units if key in ["dist border", "exclude overlap", "pad border"]: key += " px" return key, value def _parse(self): """Return full documentation Returns ------- datadict: dict of dicts Full configuration Notes ----- If a configuration section in self.path is incomplete, then the defaults are be inserted and self.path is overridden. """ with self.path.open() as fd: data = fd.readlines() outdict = {} for line in data: line = line.strip() if (line.startswith("#") or len(line) == 0): pass elif line.startswith("["): sec = line.strip("[]") outdict[sec] = {} else: key, val = line.split("=") key = key.strip() val = val.strip() # backwards compatibility: key, val = self._parse_compat(sec, key, val) val = self._check_value(sec, key, val) outdict[sec][key] = val # Insert default variables where missing must_write = False for sec in outdict: for key in definitions.config[sec]: if key not in outdict[sec]: outdict[sec][key] = definitions.config[sec][key][0] must_write = True if must_write: # Update the configuration file self._write(outdict) return outdict def _write(self, datadict): """Write configuration dictionary Parameters ---------- datadict: dict of dicts the full configuration Notes ----- The configuration key values are converted to the correct dtype before writing using the definitions given in definitions.py. """ keys = sorted(list(datadict.keys())) lines = ["# DryMass version {}".format(version), "# Configuration file documented at: ", "# https://drymass.readthedocs.io/en/stable/configuration" + "_file.html", ] for kk in keys: lines.append("") lines.append("[{}]".format(kk)) subkeys = sorted(list(datadict[kk].keys())) for sk in subkeys: value = datadict[kk][sk] typefunc = definitions.config[kk][sk][1] if value is not None: value = typefunc(value) if typefunc is parse_funcs.strlist: # cosmetics for e.g. '[roi]: ignore data' value = ", ".join(value) lines.append("{} = {}".format(sk, value)) for ii in range(len(lines)): lines[ii] += "\n" with self.path.open("w") as fd: fd.writelines(lines)
[docs] def remove_section(self, section): """Remove a section from the configuration file""" datadict = self._parse() datadict.pop(section) self._write(datadict)
[docs] def set_value(self, section, key, value): """Set a configuration key value Parameters ---------- section: str the configuration section key: str the configuration key in `section` value: the configuration key value Notes ----- Valid section and key names are defined in definitions.py """ # load, update, and save sec = self[section] sec[key] = value self[section] = sec