From fc348c3a69c3c36907ef6863748d677254ac382e Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Tue, 5 Dec 2017 17:58:52 +0100 Subject: use linted talerconfig --- talersurvey/talerconfig.py | 277 ++++++++++++++++++++++++--------------------- 1 file changed, 149 insertions(+), 128 deletions(-) (limited to 'talersurvey') diff --git a/talersurvey/talerconfig.py b/talersurvey/talerconfig.py index ba4dfbb..a7ca065 100644 --- a/talersurvey/talerconfig.py +++ b/talersurvey/talerconfig.py @@ -18,23 +18,22 @@ Parse GNUnet-style configurations in pure Python """ -# FIXME: make sure that autovivification of config entries -# does not leave garbage behind (use weakrefs!) - import logging import collections import os import weakref +import sys +import re -logger = logging.getLogger(__name__) +LOGGER = logging.getLogger(__name__) __all__ = ["TalerConfig"] -taler_datadir = None +TALER_DATADIR = None try: # not clear if this is a good idea ... - from talerpaths import taler_datadir as t - taler_datadir = t + from talerpaths import TALER_DATADIR as t + TALER_DATADIR = t except ImportError: pass @@ -45,7 +44,7 @@ class ExpansionSyntaxError(Exception): pass -def expand(s, getter): +def expand(var, getter): """ Do shell-style parameter expansion. Supported syntax: @@ -56,18 +55,18 @@ def expand(s, getter): pos = 0 result = "" while pos != -1: - start = s.find("$", pos) + start = var.find("$", pos) if start == -1: break - if s[start:].startswith("${"): + if var[start:].startswith("${"): balance = 1 end = start + 2 - while balance > 0 and end < len(s): - balance += {"{": 1, "}": -1}.get(s[end], 0) + while balance > 0 and end < len(var): + balance += {"{": 1, "}": -1}.get(var[end], 0) end += 1 if balance != 0: raise ExpansionSyntaxError("unbalanced parentheses") - piece = s[start+2:end-1] + piece = var[start+2:end-1] if piece.find(":-") > 0: varname, alt = piece.split(":-", 1) replace = getter(varname) @@ -77,20 +76,20 @@ def expand(s, getter): varname = piece replace = getter(varname) if replace is None: - replace = s[start:end] + replace = var[start:end] else: end = start + 2 - while end < len(s) and s[start+1:end+1].isalnum(): + while end < len(var) and var[start+1:end+1].isalnum(): end += 1 - varname = s[start+1:end] + varname = var[start+1:end] replace = getter(varname) if replace is None: - replace = s[start:end] + replace = var[start:end] result = result + replace pos = end - return result + s[pos:] + return result + var[pos:] class OptionDict(collections.defaultdict): @@ -99,79 +98,81 @@ class OptionDict(collections.defaultdict): self.section_name = section_name super().__init__() def __missing__(self, key): - e = Entry(self.config(), self.section_name, key) - self[key] = e - return e - def __getitem__(self, slice): - return super().__getitem__(slice.lower()) - def __setitem__(self, slice, value): - super().__setitem__(slice.lower(), value) + entry = Entry(self.config(), self.section_name, key) + self[key] = entry + return entry + def __getitem__(self, chunk): + return super().__getitem__(chunk.lower()) + def __setitem__(self, chunk, value): + super().__setitem__(chunk.lower(), value) class SectionDict(collections.defaultdict): - def __init__(self): - super().__init__() def __missing__(self, key): - v = OptionDict(self, key) - self[key] = v - return v - def __getitem__(self, slice): - return super().__getitem__(slice.lower()) - def __setitem__(self, slice, value): - super().__setitem__(slice.lower(), value) + value = OptionDict(self, key) + self[key] = value + return value + def __getitem__(self, chunk): + return super().__getitem__(chunk.lower()) + def __setitem__(self, chunk, value): + super().__setitem__(chunk.lower(), value) class Entry: - def __init__(self, config, section, option, value=None, filename=None, lineno=None): - self.value = value - self.filename = filename - self.lineno = lineno + def __init__(self, config, section, option, **kwargs): + self.value = kwargs.get("value") + self.filename = kwargs.get("filename") + self.lineno = kwargs.get("lineno") self.section = section self.option = option self.config = weakref.ref(config) def __repr__(self): - return "" % (self.section, self.option, repr(self.value),) + return "" \ + % (self.section, self.option, repr(self.value),) def __str__(self): return self.value - def value_string(self, default=None, warn=False, required=False): + def value_string(self, default=None, required=False, warn=False): if required and self.value is None: - raise ConfigurationError("Missing required option '%s' in section '%s'" % (self.option.upper(), self.section.upper())) + raise ConfigurationError("Missing required option '%s' in section '%s'" \ + % (self.option.upper(), self.section.upper())) if self.value is None: if warn: if default is not None: - logger.warn("Configuration is missing option '%s' in section '%s', falling back to '%s'", - self.option, self.section, default) + LOGGER.warning("Configuration is missing option '%s' in section '%s',\ + falling back to '%s'", self.option, self.section, default) else: - logger.warn("Configuration is missing option '%s' in section '%s'", self.option.upper(), self.section.upper()) + LOGGER.warning("Configuration ** is missing option '%s' in section '%s'", + self.option.upper(), self.section.upper()) return default return self.value - def value_int(self, default=None, warn=False, required=False): - v = self.value_string(default, warn, required) - if v is None: + def value_int(self, default=None, required=False, warn=False): + value = self.value_string(default, warn, required) + if value is None: return None try: - return int(v) + return int(value) except ValueError: - raise ConfigurationError("Expected number for option '%s' in section '%s'" % (self.option.upper(), self.section.upper())) + raise ConfigurationError("Expected number for option '%s' in section '%s'" \ + % (self.option.upper(), self.section.upper())) def _getsubst(self, key): - x = self.config()["paths"][key].value - if x is not None: - return x - x = os.environ.get(key) - if x is not None: - return x + value = self.config()["paths"][key].value + if value is not None: + return value + value = os.environ.get(key) + if value is not None: + return value return None - def value_filename(self, default=None, warn=False, required=False): - v = self.value_string(default, warn, required) - if v is None: + def value_filename(self, default=None, required=False, warn=False): + value = self.value_string(default, required, warn) + if value is None: return None - return expand(v, lambda x: self._getsubst(x)) + return expand(value, self._getsubst) def location(self): if self.filename is None or self.lineno is None: @@ -190,6 +191,8 @@ class TalerConfig: """ self.sections = SectionDict() + # defaults != config file: the first is the 'base' + # whereas the second overrides things from the first. @staticmethod def from_file(filename=None, load_defaults=True): cfg = TalerConfig() @@ -204,14 +207,17 @@ class TalerConfig: cfg.load_file(filename) return cfg - def value_string(self, section, option, default=None, required=None, warn=False): - return self.sections[section][option].value_string(default, required, warn) + def value_string(self, section, option, **kwargs): + return self.sections[section][option].value_string( + kwargs.get("default"), kwargs.get("required"), kwargs.get("warn")) - def value_filename(self, section, option, default=None, required=None, warn=False): - return self.sections[section][option].value_filename(default, required, warn) + def value_filename(self, section, option, **kwargs): + return self.sections[section][option].value_filename( + kwargs.get("default"), kwargs.get("required"), kwargs.get("warn")) - def value_int(self, section, option, default=None, required=None, warn=False): - return self.sections[section][option].value_int(default, required, warn) + def value_int(self, section, option, **kwargs): + return self.sections[section][option].value_int( + kwargs.get("default"), kwargs.get("required"), kwargs.get("warn")) def load_defaults(self): base_dir = os.environ.get("TALER_BASE_CONFIG") @@ -220,12 +226,15 @@ class TalerConfig: return prefix = os.environ.get("TALER_PREFIX") if prefix: + tmp = os.path.split(os.path.normpath(prefix)) + if re.match("lib", tmp[1]): + prefix = tmp[0] self.load_dir(os.path.join(prefix, "share/taler/config.d")) return - if taler_datadir: - self.load_dir(os.path.join(taler_datadir, "share/taler/config.d")) + if TALER_DATADIR: + self.load_dir(os.path.join(TALER_DATADIR, "share/taler/config.d")) return - logger.warn("no base directory found") + LOGGER.warning("no base directory found") @staticmethod def from_env(*args, **kwargs): @@ -240,7 +249,7 @@ class TalerConfig: try: files = os.listdir(dirname) except FileNotFoundError: - logger.warn("can't read config directory '%s'", dirname) + LOGGER.warning("can't read config directory '%s'", dirname) return for file in files: if not file.endswith(".conf"): @@ -249,73 +258,85 @@ class TalerConfig: def load_file(self, filename): sections = self.sections - with open(filename, "r") as file: - lineno = 0 - current_section = None - for line in file: - lineno += 1 - line = line.strip() - if len(line) == 0: - # empty line - continue - if line.startswith("#"): - # comment - continue - if line.startswith("["): - if not line.endswith("]"): - logger.error("invalid section header in line %s: %s", lineno, repr(line)) - section_name = line.strip("[]").strip().strip('"') - current_section = section_name - continue - if current_section is None: - logger.error("option outside of section in line %s: %s", lineno, repr(line)) - continue - kv = line.split("=", 1) - if len(kv) != 2: - logger.error("invalid option in line %s: %s", lineno, repr(line)) - key = kv[0].strip() - value = kv[1].strip() - if value.startswith('"'): - value = value[1:] - if not value.endswith('"'): - logger.error("mismatched quotes in line %s: %s", lineno, repr(line)) - else: - value = value[:-1] - e = Entry(self.sections, current_section, key, value, filename, lineno) - sections[current_section][key] = e + try: + with open(filename, "r") as file: + lineno = 0 + current_section = None + for line in file: + lineno += 1 + line = line.strip() + if line == "": + # empty line + continue + if line.startswith("#"): + # comment + continue + if line.startswith("["): + if not line.endswith("]"): + LOGGER.error("invalid section header in line %s: %s", + lineno, repr(line)) + section_name = line.strip("[]").strip().strip('"') + current_section = section_name + continue + if current_section is None: + LOGGER.error("option outside of section in line %s: %s", lineno, repr(line)) + continue + pair = line.split("=", 1) + if len(pair) != 2: + LOGGER.error("invalid option in line %s: %s", lineno, repr(line)) + key = pair[0].strip() + value = pair[1].strip() + if value.startswith('"'): + value = value[1:] + if not value.endswith('"'): + LOGGER.error("mismatched quotes in line %s: %s", lineno, repr(line)) + else: + value = value[:-1] + entry = Entry(self.sections, current_section, key, + value=value, filename=filename, lineno=lineno) + sections[current_section][key] = entry + except FileNotFoundError: + LOGGER.error("Configuration file (%s) not found", filename) + sys.exit(3) def dump(self): - for section_name, section in self.sections.items(): - print("[%s]" % (section.section_name,)) - for option_name, e in section.items(): - print("%s = %s # %s" % (e.option, e.value, e.location())) - - def __getitem__(self, slice): - if isinstance(slice, str): - return self.sections[slice] + for kv_section in self.sections.items(): + print("[%s]" % (kv_section[1].section_name,)) + for kv_option in kv_section[1].items(): + print("%s = %s # %s" % \ + (kv_option[1].option, + kv_option[1].value, + kv_option[1].location())) + + def __getitem__(self, chunk): + if isinstance(chunk, str): + return self.sections[chunk] raise TypeError("index must be string") if __name__ == "__main__": - import sys import argparse - parser = argparse.ArgumentParser() - parser.add_argument("--section", "-s", dest="section", default=None, metavar="SECTION") - parser.add_argument("--option", "-o", dest="option", default=None, metavar="OPTION") - parser.add_argument("--config", "-c", dest="config", default=None, metavar="FILE") - parser.add_argument("--filename", "-f", dest="expand_filename", default=False, action='store_true') - args = parser.parse_args() - - tc = TalerConfig.from_file(args.config) - - if args.section is not None and args.option is not None: - if args.expand_filename: - x = tc.value_filename(args.section, args.option) + PARSER = argparse.ArgumentParser() + PARSER.add_argument("--section", "-s", dest="section", + default=None, metavar="SECTION") + PARSER.add_argument("--option", "-o", dest="option", + default=None, metavar="OPTION") + PARSER.add_argument("--config", "-c", dest="config", + default=None, metavar="FILE") + PARSER.add_argument("--filename", "-f", dest="expand_filename", + default=False, action='store_true') + ARGS = PARSER.parse_args() + + TC = TalerConfig.from_file(ARGS.config) + + if ARGS.section is not None and ARGS.option is not None: + if ARGS.expand_filename: + X = TC.value_filename(ARGS.section, ARGS.option) else: - x = tc.value_string(args.section, args.option) - if x is not None: - print(x) + X = TC.value_string(ARGS.section, ARGS.option) + if X is not None: + print(X) else: - tc.dump() + TC.dump() -- cgit v1.2.3