Source code for woom.conf

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Configurations related utilities based on the :mod:`configobj` system
"""

import logging
import pathlib
import pprint
import re

import configobj
import pandas as pd

try:
    import configobj.validate as validate
except ImportError:
    import validate

from . import util as wutil
from .__init__ import WoomError

CACHE = {"cfgspecs": {}}


[docs] class WoomConfigError(WoomError): pass
[docs] def is_path(value): """Convert to :class:`pathlib.Path`""" if value is None: return try: return pathlib.Path(value) except Exception as e: raise WoomConfigError("Can't convert config value to path: " + e.args[0])
[docs] def is_datetime(value, round=None): """Convert to :class:`pandas.Timestamp`""" if value is None: return # utc = validate.is_boolean(utc) round = None if str(round).lower() == "none" else round try: return wutil.WoomDate(value, round=round) except Exception as e: raise WoomConfigError("Can't convert config value to datetime: " + e.args[0])
[docs] def is_timedelta(value): """Convert to :class:`pandas.Timedelta`""" if value is None: return try: return pd.to_timedelta(value) except Exception as e: raise WoomConfigError("Can't convert config value to timedelta: " + e.args[0])
[docs] def is_pages(values): """Convert a one-based page-like selection .. note:: Multi-selections are converted to zero-based slices Parameters ---------- values: str, list(str) Example ------ .. ipython:: python :okwarning: @suppress from woom.conf import is_pages is_pages("4,5,7-") """ if values is None: return if not isinstance(values, list): values = [values] re_split_c = re.compile(r"\s*,\s*").split sels = [] for v in values: sels.extend(re_split_c(v)) out = [] re_split_t = re.compile(r"\s*\-\s*").split for sel in sels: if isinstance(sel, str) and "-" in sel: ssel = [ (int(s) if i > 0 else int(s) - 1) if s != "" else None for i, s in enumerate(re_split_t(sel)) ] out.append(slice(*ssel)) else: out.append(int(sel)) return out
#: Default woom validator fonctions VALIDATOR_FUNCTIONS = { "path": is_path, "datetime": is_datetime, "timedelta": is_timedelta, "pages": is_pages, }
[docs] def get_validator(): """Get a :class:`configobj.validate.Validator` instance""" return validate.Validator(VALIDATOR_FUNCTIONS)
# if "validator" not in CACHE: # CACHE["validator"] = validate.Validator(VALIDATOR_FUNCTIONS) # return CACHE["validator"]
[docs] def get_cfgspecs(cfgspecsfiles): """Get a configuration specification instance from a list of files""" cfgspecs = None for cfgspecsfile in cfgspecsfiles if isinstance(cfgspecsfiles, list) else [cfgspecsfiles]: this_cfgspecs = configobj.ConfigObj(cfgspecsfile, interpolation=False, list_values=False) if cfgspecs is None: cfgspecs = this_cfgspecs else: cfgspecs.merge(this_cfgspecs) return cfgspecs
# if name not in CACHE["cfgspecs"]: # CACHE["cfgspecs"][name] = # return CACHE["cfgspecs"][name]
[docs] def load_cfg(cfgfile, cfgspecsfiles, list_values=True, interpolation=True): """Get a validated :class:`configobj.configObj` instance""" validator = get_validator() cfgspecs = get_cfgspecs(cfgspecsfiles) cfg = configobj.ConfigObj( cfgfile or {}, configspec=cfgspecs, interpolation=interpolation, list_values=list_values, ) success = cfg.validate(validator, preserve_errors=True) if success is not True: msg = f"Error while validating config: {cfgfile}\n" msg += pprint.pformat(success) logging.getLogger(__name__).error(msg) raise WoomConfigError(msg) return cfg
[docs] def strip_out_sections(cfg): """Remove all sections keeping only scalars""" cfgo = configobj.ConfigObj(cfg, interpolation=cfg.main.interpolation) for key in cfg.sections: del cfgo[key] return cfgo
[docs] def keep_sections(cfg): """Only keep section""" cfgo = configobj.ConfigObj(cfg, interpolation=cfg.main.interpolation) for key in cfg.scalars: del cfgo[key] return cfgo
[docs] def merge_args_with_config(cfg, args, names, prefix=None): """Merge parser arguments with configuration items .. note:: The configuration is modified in place Parameters ---------- cfg: configobj.ConfigObj args: argparse.Namespace names: list(str) prefix: str String to prepend to names in `args` """ prefix = prefix or "" for name in names: value = getattr(args, prefix + name, None) if value is not None: cfg[name] = value
[docs] def inherit_cfg(cfg, inherit_from): """Inherit content from another config""" for key, val in list(inherit_from.items()): if key not in cfg or cfg[key] is None: cfg[key] = val elif isinstance(cfg[key], dict) and isinstance(val, dict): inherit_cfg(cfg[key], val)